//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#include "RuntimeByteCodePch.h"

#include "RegexCommon.h"
#include "RegexPattern.h"
#include "Library/RegexHelper.h"

#include "DataStructures/Option.h"
#include "DataStructures/ImmutableList.h"
#include "DataStructures/BufferBuilder.h"
#include "ByteCode/OpCodeUtilAsmJs.h"
#include "ByteCode/ByteCodeSerializer.h"

#include "Language/AsmJsModule.h"
#include "Library/ES5Array.h"

void ChakraBinaryBuildDateTimeHash(DWORD * buildDateHash, DWORD * buildTimeHash);

namespace Js
{
    const int magicConstant = *(int*)"ChBc";
    const int majorVersionConstant = 1;
    const int minorVersionConstant = 1;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
    // These magic constants can be enabled to bracket and check different sections of the serialization
    // file.  Turn on BYTE_CODE_MAGIC_CONSTANTS in ByteCodeSerializer.h to enable this.
    const int magicStartOfFunctionBody = *(int*)"fun[";
    const int magicEndOfFunctionBody = *(int*)"]fun";
    const int magicStartOfConstantTable = *(int*)"con[";
    const int magicEndOfConstantTable = *(int*)"]con";
    const int magicStartStringConstant = *(int*)"str[";
    const int magicEndStringConstant = *(int*)"]str";
    const int magicStartOfCacheIdToPropIdMap = *(int*)"cid[";
    const int magicEndOfCacheIdToPropIdMap = *(int*)"]cid";
    const int magicStartOfReferencedPropIdMap = *(int*)"rid[";
    const int magicEndOfReferencedPropIdMap = *(int*)"]rid";
    const int magicStartOfPropertyIdsForScopeSlotArray = *(int*)"scp[";
    const int magicEndOfPropertyIdsForScopeSlotArray = *(int*)"]scp";
    const int magicStartOfDebuggerScopes = *(int*)"dsc[";
    const int magicEndOfDebuggerScopes = *(int*)"]dsc";
    const int magicStartOfDebuggerScopeProperties = *(int*)"dsp[";
    const int magicEndOfDebuggerScopeProperties = *(int*)"]dsp";
    const int magicStartOfAux = *(int*)"aux[";
    const int magicEndOfAux = *(int*)"]aux";
    const int magicStartOfAuxVarArray = *(int*)"ava[";
    const int magicEndOfAuxVarArray = *(int*)"]ava";
    const int magicStartOfAuxIntArray = *(int*)"aia[";
    const int magicEndOfAuxIntArray = *(int*)"]aia";
    const int magicStartOfAuxFltArray = *(int*)"afa[";
    const int magicEndOfAuxFltArray = *(int*)"]afa";
    const int magicStartOfAuxPropIdArray = *(int*)"api[";
    const int magicEndOfAuxPropIdArray = *(int*)"]api";
    const int magicStartOfAuxFuncInfoArray = *(int*)"afi[";
    const int magicEndOfAuxFuncInfoArray = *(int*)"]afi";
    const int magicStartOfAsmJsFuncInfo = *(int*)"aFI[";
    const int magicEndOfAsmJsFuncInfo = *(int*)"]aFI";
    const int magicStartOfAsmJsModuleInfo = *(int*)"ami[";
    const int magicEndOfAsmJsModuleInfo = *(int*)"]ami";
    const int magicStartOfPropIdsOfFormals = *(int*)"pif[";
    const int magicEndOfPropIdsOfFormals = *(int*)"]pif";
    const int magicStartOfSlotIdToNestedIndexArray = *(int*)"sni[";
    const int magicEndOfSlotIdToNestedIndexArray = *(int*)"]sni";
    const int magicStartOfCallSiteToCallApplyCallSiteArray = *(int*)"cca[";
    const int magicEndOfCallSiteToCallApplyCallSiteArray = *(int*)"]cca";
#endif

    // Serialized files are architecture specific
#ifndef VALIDATE_SERIALIZED_BYTECODE
#if TARGET_64
    const byte magicArchitecture = 64;
#else
    const byte magicArchitecture = 32;
#endif
#else
#if _M_AMD64
    const int magicArchitecture = *(int*)"amd";
#elif _M_IA64
    const int magicArchitecture = *(int*)"ia64";
#elif _M_ARM
    const int magicArchitecture = *(int*)"arm";
#elif _M_ARM_64
    const int magicArchitecture = *(int*)"arm64";
#else
    const int magicArchitecture = *(int*)"x86";
#endif
#endif

// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Byte Code Serializer Versioning scheme
// Version number is a GUID (128 bits). There are two versioning modes--Engineering and Release. Engineering mode is for day-to-day development. Every time chakra.dll is built a
// fresh new version is generated by hashing the build date and time. This means that a byte code file saved to disk is exactly tied to the binary that generated it. This works
// well for QA test runs and buddy tests because there is no chance of effects between runs.
//
// Release mode is used when chakra.dll is close to public release where there are actual changes to chakra. The GUID is a fixed number from build-to-build. This number will stay
// the same for releases where there is no change to chakra.dll. The reason for this is that we don't want to invalidate compatible byte code that has already been cached.
enum FileVersionScheme : byte
{
    // Currently Chakra and ChakraCore versioning scheme is different.
    // Same version number for Chakra and ChakraCore doesn't mean they are the same.
    // Give the versioning scheme different value, so that byte code generate from one won't be use in the other.
    LibraryByteCodeVersioningScheme = 0,
#ifdef NTBUILD
    EngineeringVersioningScheme = 10,
    ReleaseVersioningScheme = 20,
#else
    EngineeringVersioningScheme = 11,
    ReleaseVersioningScheme = 21,
#endif

#if (defined(NTBUILD) && CHAKRA_VERSION_RELEASE) || (!defined(NTBUILD) && CHAKRA_CORE_VERSION_RELEASE)
    CurrentFileVersionScheme = ReleaseVersioningScheme
#else
    CurrentFileVersionScheme = EngineeringVersioningScheme
#endif
};


// it should be in separate file for testing
#include "ByteCodeCacheReleaseFileVersion.h"

// Used for selective serialization of Function Body fields to make the representation compact
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) bool has_##name : 1
#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType, defaultValue) bool has_##name : 1

#define DEFINE_ALL_FIELDS
struct SerializedFieldList {
#include "SerializableFunctionFields.h"
    bool has_m_lineNumber: 1;
    bool has_m_columnNumber: 1;
    bool has_attributes : 1;
    bool has_m_nestedCount: 1;
    bool has_loopHeaderArray : 1;
    bool has_asmJsInfo : 1;
    bool has_auxiliary : 1;
    bool has_propertyIdOfFormals: 1;
    bool has_slotIdInCachedScopeToNestedIndexArray : 1;
    bool has_callSiteToCallApplyCallSiteArray : 1;
    bool has_debuggerScopeSlotArray : 1;
    bool has_deferredStubs : 1;
    bool has_scopeInfo : 1;
    bool has_printOffsets : 1;
};

C_ASSERT(sizeof(GUID)==sizeof(DWORD)*4);
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// Holds a buffer and size for use by the serializer
struct ByteBuffer
{
    uint32 byteCount;
    union
    {
        void * pv;
        const char16 * s16;
        const char * s8;
    };
public:
    ByteBuffer(uint32 byteCount, void * pv)
        : byteCount(byteCount), pv(pv)
    { }
};
} // namespace Js

template<>
struct DefaultComparer<Js::ByteBuffer*>
{
    static bool Equals(Js::ByteBuffer const * str1, Js::ByteBuffer const * str2)
    {
        if (str1->byteCount != str2->byteCount)
        {
            return false;
        }
        return memcmp(str1->pv, str2->pv, str1->byteCount)==0;
    }

    static hash_t GetHashCode(Js::ByteBuffer const * str)
    {
        return JsUtil::CharacterBuffer<char>::StaticGetHashCode(str->s8, str->byteCount);
    }
};

namespace Js
{
struct IndexEntry
{
    BufferBuilderByte* isPropertyRecord;
    int id;
};


#pragma pack(push, 1)
struct StringIndexRecord
{
    int offset;
    bool isPropertyRecord;
};
#pragma pack(pop)

typedef JsUtil::BaseDictionary<ByteBuffer*, IndexEntry, ArenaAllocator, PrimeSizePolicy, DefaultComparer> TString16ToId;

static LocalScopeInfoId InvalidLocalScopeInfoId = 0xFFFFFFFF;
typedef JsUtil::BaseDictionary<Js::ScopeInfo*, LocalScopeInfoId, ArenaAllocator> ScopeInfoToScopeInfoIdMap;

// Boolean flags on the FunctionBody
enum FunctionFlags
{
    ffIsDeclaration                    = 0x0001,
    ffHasImplicitArgsIn                = 0x0002,
    ffIsAccessor                       = 0x0004,
    ffIsGlobalFunc                     = 0x0008,
    ffDontInline                       = 0x0010,
    ffIsFuncRegistered                 = 0x0020,
    ffIsStaticNameFunction             = 0x0040,
    ffIsStrictMode                     = 0x0080,
    ffDoBackendArgumentsOptimization   = 0x0100,
    ffIsEval                           = 0x0200,
    ffIsDynamicFunction                = 0x0400,
    ffhasAllNonLocalReferenced         = 0x0800,
    ffhasSetIsObject                   = 0x1000,
    ffhasSetCallsEval                  = 0x2000,
    ffIsNameIdentifierRef              = 0x4000,
    ffChildCallsEval                   = 0x8000,
    ffHasReferenceableBuiltInArguments = 0x10000,
    ffIsNamedFunctionExpression        = 0x20000,
    ffIsAsmJsMode                      = 0x40000,
    ffIsAsmJsFunction                  = 0x80000,
    ffIsAnonymous                      = 0x100000,
    ffUsesArgumentsObject              = 0x200000,
    ffDoScopeObjectCreation            = 0x400000,
    ffIsParamAndBodyScopeMerged        = 0x800000,
    ffIsMethod                         = 0x1000000,
    ffIsClassMember                    = 0x2000000,
};

enum ScopeInfoFlags : byte
{
    sifNone                     = 0x0,
    sifIsDynamic                = 0x1,
    sifIsObject                 = 0x2,
    sifMustInstantiate          = 0x4,
    sifIsCached                 = 0x8,
    sifHasLocalInClosure        = 0x10,
    sifIsGeneratorFunctionBody  = 0x20,
    sifIsAsyncFunctionBody      = 0x40,
};

enum SymbolInfoFlags : byte
{
    syifNone                    = 0x0,
    syifHasFuncAssignment        = 0x1,
    syifIsBlockVariable          = 0x2,
    syifIsConst                  = 0x4,
    syifIsFuncExpr               = 0x8,
    syifIsModuleExportStorage    = 0x10,
    syifIsModuleImport           = 0x20,
};

// Kinds of constant
enum ConstantType : byte
{
    ctInt8 = 1,
    ctInt16 = 2,
    ctInt32 = 3,
    ctNumber = 4,
    ctString16 = 5,
    ctPropertyString16 = 6,
    ctNull = 7,
    ctUndefined = 8,
    ctNullDisplay = 9,
    ctStrictNullDisplay = 10,
    ctTrue = 11,
    ctFalse = 12,
    ctStringTemplateCallsite = 13,
};


// Try to convert from size_t to uint32. May overflow (and return false) on 64-bit.
bool TryConvertToUInt32(size_t size, uint32 * out)
{
    *out = (uint32)size;
    if (sizeof(size) == sizeof(uint32))
    {
        return true;
    }
    Assert(sizeof(size_t) == sizeof(uint64));
    if((uint64)(*out) == size)
    {
        return true;
    }
    AssertMsg(false, "Is it really an offset greater than 32 bits?"); // More likely a bug somewhere.
    return false;
}

#if VARIABLE_INT_ENCODING
template <typename T>
static const byte * ReadVariableInt(const byte * buffer, size_t remainingBytes, T * value)
{
    Assert(remainingBytes >= sizeof(byte));
    byte firstByte = *(byte*) buffer;

    if (firstByte >= MIN_SENTINEL)
    {
        Assert(remainingBytes >= sizeof(uint16));
        const byte* locationOfValue = buffer + 1;

        if (firstByte == TWO_BYTE_SENTINEL)
        {
            uint16 twoByteValue = *((serialization_alignment uint16*) locationOfValue);
            Assert(twoByteValue > ONE_BYTE_MAX);

            *value = twoByteValue;
            PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, _u("TestTrace: VariableIntEncoding (decode)- 2 bytes, value %u\n"), *value);
            return buffer + sizeof(uint16) +SENTINEL_BYTE_COUNT;
        }
        else
        {
            Assert(remainingBytes >= sizeof(T));
            Assert(firstByte == FOUR_BYTE_SENTINEL);
            *value = *((serialization_alignment T*) locationOfValue);
            Assert(*value > TWO_BYTE_MAX || *value <= 0);

            PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, _u("TestTrace: VariableIntEncoding (decode) - 4 bytes, value %u\n"), *value);
            return buffer + sizeof(T) +SENTINEL_BYTE_COUNT;
        }
    }
    else
    {
        *value = (T) firstByte;
        PHASE_PRINT_TESTTRACE1(Js::VariableIntEncodingPhase, _u("TestTrace: VariableIntEncoding (decode) - 1 byte, value %u\n"), *value);
        return buffer + sizeof(byte);
    }
}
#endif

// Compile-time-check some invariants that the file format depends on
C_ASSERT(sizeof(PropertyId)==sizeof(int32));


// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//  Byte Code File Header Layout
//  Offset      Size    Name                                    Value
//  0       4       Magic Number                                "ChBc"
//  4       4       Total File Size
//  8       1       File Version Scheme                     10 for engineering      20 for release
//  9       4       Version DWORD 1                             jscript minor version   GUID quad part 1
//  13      4       Version DWORD 2                             jscript major version   GUID quad part 2
//  17      4       Version DWORD 3                             hash of __DATE__            GUID quad part 3
//  21      4       Version DWORD 4                             hash of __TIME__            GUID quad part 4
//  25      4       Expected Architecture                       "amd"0, "ia64", "arm"0 or "x86"0
//  29      4       Expected Function Body Size
//  33      4       Expected Built In PropertyCount
//  37      4       Expected Op Code Count
//  41      4       Size of Original Source Code
//  45      4       Count of Auxiliary Structures
//  49      4       Smallest Literal Object ID
//  53      4       Largest Literal Object ID
//  57      4       Offset from start of this file
//                    to Strings Table
//  61      4       Offset to Source Spans
//  65      4       Count of Functions
//  69      4       Offset to Functions
//  73      4       Offset to Auxiliary Structures
//  77      4       Count of Strings
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// This is the serializer
class ByteCodeBufferBuilder
{
    // Begin File Layout -------------------------------
    ConstantSizedBufferBuilderOf<int32> magic;
    ConstantSizedBufferBuilderOf<int32> totalSize;  // The size is unknown when the offsets are calculated so just reserve 4 bytes for this for now to avoid doing two passes to calculate the offsets
    BufferBuilderByte fileVersionKind; // Engineering or Release
    ConstantSizedBufferBuilderOf<int32> V1; // V1-V4 are the parts of the version. It is a fixed version GUID or a per-build version.
    ConstantSizedBufferBuilderOf<int32> V2;
    ConstantSizedBufferBuilderOf<int32> V3;
    ConstantSizedBufferBuilderOf<int32> V4;
    BufferBuilderInt32 architecture;
    BufferBuilderInt32 expectedFunctionBodySize;
    BufferBuilderInt32 expectedBuildInPropertyCount;
    BufferBuilderInt32 expectedOpCodeCount;
    BufferBuilderInt32 originalSourceSize;
    BufferBuilderInt32 originalCharLength;
    BufferBuilderRelativeOffset string16sOffset;
    BufferBuilderRelativeOffset lineInfoCacheOffset;
    BufferBuilderRelativeOffset functionsOffset;
    BufferBuilderRelativeOffset scopeInfoOffset;
    BufferBuilderInt32 string16Count;
    BufferBuilderList string16IndexTable;
    BufferBuilderList string16Table;
    BufferBuilderAligned alignedString16Table;
    BufferBuilderInt32 lineInfoCacheCount;
    BufferBuilderRaw lineCharacterOffsetCacheBuffer;
    BufferBuilderByte lineInfoHasByteCache;
    BufferBuilderRaw lineByteOffsetCacheBuffer;
    BufferBuilderInt32 functionCount;
    BufferBuilderList functionsTable;
    BufferBuilderInt32 scopeInfoCount;
    BufferBuilderList scopeInfoTable;
    BufferBuilderList scopeInfoRelativeOffsets;
    // End File Layout ---------------------------------
    ArenaAllocator * alloc;
    TString16ToId * string16ToId;
    ScopeInfoToScopeInfoIdMap* scopeInfoToScopeInfoIdMap;
    int nextString16Id;
    int topFunctionId;
    LPCUTF8 utf8Source;
    ScriptContext * scriptContext;
    BufferBuilder * startOfCachedScopeAuxBlock;
    DWORD dwFlags;

    //Instead of referencing TotalNumberOfBuiltInProperties directly; or PropertyIds::_countJSOnlyProperty we use this.
    //For library code this will be set to _countJSOnlyProperty and for normal bytecode this will be TotalNumberOfBuiltInProperties
    int builtInPropertyCount;

    bool GenerateLibraryByteCode() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_BUFFER_LIBRARY) != 0;
    }

    bool GenerateByteCodeForNative() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_FOR_NATIVE) != 0;
    }

    bool GenerateParserStateCache() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_PARSER_STATE) != 0;
    }

    bool ShouldAllocWithCoTaskMem() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_COTASKMEMALLOC);
    }

    bool ShouldAllocWithANew() const
    {
        return (dwFlags & GENERATE_BYTE_CODE_ALLOC_ANEW);
    }

public:

    ByteCodeBufferBuilder(uint32 sourceSize, uint32 sourceCharLength, LPCUTF8 utf8Source, Utf8SourceInfo* sourceInfo, ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD dwFlags, int builtInPropertyCount)
        : magic(_u("Magic"), magicConstant),
          totalSize(_u("Total Size"), 0),
          fileVersionKind(_u("FileVersionKind"), 0),
          V1(_u("V1"), 0),
          V2(_u("V2"), 0),
          V3(_u("V3"), 0),
          V4(_u("V4"), 0),
          architecture(_u("Expected Architecture"), magicArchitecture),
          expectedFunctionBodySize(_u("Expected Function Body Size"), sizeof(unaligned FunctionBody)),
          expectedBuildInPropertyCount(_u("Expected Built-in Properties"), builtInPropertyCount),
          expectedOpCodeCount(_u("Expected Number of OpCodes"),  (int)OpCode::Count),
          originalSourceSize(_u("Source Size"), sourceSize),
          originalCharLength(_u("Source Char Length"), sourceCharLength),
          string16sOffset(_u("Offset of String16s"), &string16Count),
          lineInfoCacheOffset(_u("Offset of Line Info Cache"), &lineInfoCacheCount),
          functionCount(_u("Function Count"), 0),
          functionsOffset(_u("Offset of Functions"), &functionCount),
          string16Count(_u("String16 Count"), 0),
          string16IndexTable(_u("String16 Indexes")),
          string16Table(_u("String16 Table")),
          alignedString16Table(_u("Alignment for String16 Table"), &string16Table, sizeof(char16)),
          lineInfoCacheCount(_u("Line Info Cache"), sourceInfo->GetLineOffsetCache()->GetLineCount()),
          lineCharacterOffsetCacheBuffer(_u("Line Info Character Cache"), lineInfoCacheCount.value * sizeof(charcount_t), (byte *)sourceInfo->GetLineOffsetCache()->GetLineCharacterOffsetBuffer()),
          lineInfoHasByteCache(_u("Line Info Has Byte Cache"), sourceInfo->GetLineOffsetCache()->GetLineByteOffsetBuffer() != nullptr),
          lineByteOffsetCacheBuffer(_u("Line Info Byte Cache"), lineInfoCacheCount.value * sizeof(charcount_t), (byte *)sourceInfo->GetLineOffsetCache()->GetLineByteOffsetBuffer()),
          functionsTable(_u("Functions")),
          scopeInfoOffset(_u("Offset of ScopeInfos"), &scopeInfoCount),
          scopeInfoCount(_u("ScopeInfo Count"), 0),
          scopeInfoRelativeOffsets(_u("ScopeInfo Relative Offsets")),
          scopeInfoTable(_u("ScopeInfo Table")),
          nextString16Id(builtInPropertyCount), // Reserve the built-in property ids
          topFunctionId(0),
          utf8Source(utf8Source),
          scriptContext(scriptContext),
          startOfCachedScopeAuxBlock(nullptr),
          alloc(alloc),
          dwFlags(dwFlags),
          builtInPropertyCount(builtInPropertyCount)
    {
        if (GenerateLibraryByteCode())
        {
            expectedFunctionBodySize.value = 0;
            expectedOpCodeCount.value = 0;
#ifdef ENABLE_TEST_HOOKS
            if (scriptContext->GetConfig()->Force32BitByteCode())
            {
                architecture.value = 32;
            }
#endif
        }

        // Library bytecode uses its own scheme
        byte actualFileVersionScheme = GenerateLibraryByteCode() ? LibraryByteCodeVersioningScheme : CurrentFileVersionScheme;
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema)
        {
            actualFileVersionScheme = (byte)Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema;
        }
#endif

        fileVersionKind.value = actualFileVersionScheme;
        switch (actualFileVersionScheme)
        {
        case EngineeringVersioningScheme:
            {
                Assert(!GenerateLibraryByteCode());
                DWORD jscriptMajor, jscriptMinor, buildDateHash, buildTimeHash;
                Js::VerifyOkCatastrophic(AutoSystemInfo::GetJscriptFileVersion(&jscriptMajor, &jscriptMinor, &buildDateHash, &buildTimeHash));
                V1.value = jscriptMajor;
                V2.value = jscriptMinor;
                V3.value = buildDateHash;
                V4.value = buildTimeHash;
                break;
            }

        case ReleaseVersioningScheme:
            {
                Assert(!GenerateLibraryByteCode());
                auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
                V1.value = guidDWORDs[0];
                V2.value = guidDWORDs[1];
                V3.value = guidDWORDs[2];
                V4.value = guidDWORDs[3];
                break;
            }

        case LibraryByteCodeVersioningScheme:
            {
                Assert(GenerateLibraryByteCode());
                // To keep consistent library code between Chakra.dll and ChakraCore.dll, use a fixed version.
                // This goes hand in hand with the bytecode verification unit tests.
                V1.value = 0;
                V2.value = 0;
                V3.value = 0;
                V4.value = 0;
                break;
            }

        default:
            Throw::InternalError();
            break;
        }

#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion)
        {
            V1.value = Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion;
            V2.value = 0;
            V3.value = 0;
            V4.value = 0;
        }
#endif
        string16ToId = Anew(alloc, TString16ToId, alloc);
        scopeInfoToScopeInfoIdMap = Anew(alloc, ScopeInfoToScopeInfoIdMap, alloc);
    }

    HRESULT Create(byte ** buffer, DWORD * bufferBytes)
    {
        BufferBuilderList all(_u("Final"));

        // Reverse the lists
        string16IndexTable.list = string16IndexTable.list->ReverseCurrentList();
        string16Table.list = string16Table.list->ReverseCurrentList();
        scopeInfoTable.list = scopeInfoTable.list->ReverseCurrentList();
        scopeInfoRelativeOffsets.list = scopeInfoRelativeOffsets.list->ReverseCurrentList();

        // Prepend all sections (in reverse order because of prepend)
        all.list = regex::ImmutableList<Js::BufferBuilder*>::OfSingle(&scopeInfoTable, alloc);
        all.list = all.list->Prepend(&scopeInfoRelativeOffsets, alloc);
        all.list = all.list->Prepend(&scopeInfoCount, alloc);
        all.list = all.list->Prepend(&functionsTable, alloc);
        all.list = all.list->Prepend(&functionCount, alloc);
        if (lineByteOffsetCacheBuffer.raw != nullptr)
        {
            all.list = all.list->Prepend(&lineByteOffsetCacheBuffer, alloc);
        }
        all.list = all.list->Prepend(&lineInfoHasByteCache, alloc);
        all.list = all.list->Prepend(&lineCharacterOffsetCacheBuffer, alloc);
        all.list = all.list->Prepend(&lineInfoCacheCount, alloc);
        all.list = all.list->Prepend(&alignedString16Table, alloc);
        all.list = all.list->Prepend(&string16IndexTable, alloc);
        all.list = all.list->Prepend(&string16Count, alloc);
        all.list = all.list->Prepend(&scopeInfoOffset, alloc);
        all.list = all.list->Prepend(&functionsOffset, alloc);
        all.list = all.list->Prepend(&lineInfoCacheOffset, alloc);
        all.list = all.list->Prepend(&string16sOffset, alloc);
        all.list = all.list->Prepend(&originalCharLength, alloc);
        all.list = all.list->Prepend(&originalSourceSize, alloc);
        all.list = all.list->Prepend(&expectedOpCodeCount, alloc);
        all.list = all.list->Prepend(&expectedBuildInPropertyCount, alloc);
        all.list = all.list->Prepend(&expectedFunctionBodySize, alloc);
        all.list = all.list->Prepend(&architecture, alloc);
        all.list = all.list->Prepend(&V4, alloc);
        all.list = all.list->Prepend(&V3, alloc);
        all.list = all.list->Prepend(&V2, alloc);
        all.list = all.list->Prepend(&V1, alloc);
        all.list = all.list->Prepend(&fileVersionKind, alloc);
        all.list = all.list->Prepend(&totalSize, alloc);
        all.list = all.list->Prepend(&magic, alloc);

        // Get the string count.
        string16Count.value = nextString16Id - this->builtInPropertyCount;

        // Figure out the size and set all individual offsets
        DWORD size = all.FixOffset(0);
        totalSize.value = size;

        // Allocate the bytes
        if (ShouldAllocWithANew() || ShouldAllocWithCoTaskMem())
        {
            *bufferBytes = size;
            if (ShouldAllocWithANew())
            {
                *buffer = AnewArray(scriptContext->SourceCodeAllocator(), byte, *bufferBytes);
            }
            else
            {
                Assert(ShouldAllocWithCoTaskMem());
                *buffer = (byte*)CoTaskMemAlloc(*bufferBytes);
            }

            if (*buffer == nullptr)
            {
                return E_OUTOFMEMORY;
            }
        }

        if (size > *bufferBytes)
        {
            *bufferBytes = size;
            return *buffer == nullptr ? S_OK : E_INVALIDARG;
        }
        else
        {
            // Write into the buffer
            all.Write(*buffer, *bufferBytes);
            *bufferBytes = size;
            DebugOnly(Output::Flush());         // Flush trace
            return S_OK;
        }
    }

    bool isBuiltinProperty(PropertyId pid) {
        if (pid < this->builtInPropertyCount || pid==/*nil*/0xffffffff)
        {
            return true;
        }
        return false;
    };

    PropertyId encodeNonBuiltinPropertyId(PropertyId id) {
        const PropertyRecord * propertyValue = nullptr;
        Assert(id >= this->builtInPropertyCount); // Shouldn't have gotten a builtin property id
        propertyValue = scriptContext->GetPropertyName(id);
        id = GetIdOfPropertyRecord(propertyValue) - this->builtInPropertyCount;
        return id ^ SERIALIZER_OBSCURE_NONBUILTIN_PROPERTY_ID;
    };

    PropertyId encodePossiblyBuiltInPropertyId(PropertyId id) {
        const PropertyRecord * propertyValue = nullptr;
        if(id >= this->builtInPropertyCount)
        {
            propertyValue = scriptContext->GetPropertyName(id);
            id = GetIdOfPropertyRecord(propertyValue);
        }
        return id ^ SERIALIZER_OBSCURE_PROPERTY_ID;
    };

    int GetString16Id(ByteBuffer * bb, bool isPropertyRecord = false)
    {
        IndexEntry indexEntry;
        if (!string16ToId->TryGetValue(bb, &indexEntry))
        {
            auto sizeInBytes = bb->byteCount;
            auto stringEntry = Anew(alloc, BufferBuilderRaw, _u("String16"), sizeInBytes, (const byte *)bb->pv); // Writing the terminator even though it is computable so that this memory can be used as-is when deserialized
            string16Table.list = string16Table.list->Prepend(stringEntry, alloc);
            if (string16IndexTable.list == nullptr)
            {
                // First item in the list is the first string.
                auto stringIndexEntry = Anew(alloc, BufferBuilderRelativeOffset, _u("First String16 Index"), stringEntry);
                string16IndexTable.list = regex::ImmutableList<Js::BufferBuilder*>::OfSingle(stringIndexEntry, alloc);
                PrependByte(string16IndexTable, _u("isPropertyRecord"), (BYTE)isPropertyRecord);
            }

            // Get a pointer to the previous entry of isPropertyRecord
            indexEntry.isPropertyRecord = static_cast<BufferBuilderByte*>(string16IndexTable.list->First());

            // Subsequent strings indexes point one past the end. This way, the size is always computable by subtracting indexes.
            auto stringIndexEntry = Anew(alloc, BufferBuilderRelativeOffset, _u("String16 Index"), stringEntry, sizeInBytes);
            string16IndexTable.list = string16IndexTable.list->Prepend(stringIndexEntry, alloc);

            // By default, mark the next string to be not a property record.
            PrependByte(string16IndexTable, _u("isPropertyRecord"), (BYTE)false);

            indexEntry.id = nextString16Id;
            string16ToId->Add(bb, indexEntry);
            ++nextString16Id;
        }
        // A string might start off as not being a property record and later becoming one. Hence,
        // we set only if the transition is from false => true. Once it is a property record, it cannot go back.
        if(isPropertyRecord)
        {
            indexEntry.isPropertyRecord->value = isPropertyRecord;
        }
        return indexEntry.id;
    }

    uint32 PrependRelativeOffset(BufferBuilderList & builder, LPCWSTR clue, BufferBuilder * pointedTo)
    {
        auto entry = Anew(alloc, BufferBuilderRelativeOffset, clue, pointedTo, 0);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(int32);
    }

    uint32 PrependInt16(BufferBuilderList & builder, LPCWSTR clue, int16 value, BufferBuilderInt16 ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, BufferBuilderInt16, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int16);
    }

    uint32 PrependInt32(BufferBuilderList & builder, LPCWSTR clue, int value, BufferBuilderInt32 ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, BufferBuilderInt32, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int32);
    }

    uint32 PrependConstantInt16(BufferBuilderList & builder, LPCWSTR clue, int16 value, ConstantSizedBufferBuilderOf<int16> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int16>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int16);
    }

    uint32 PrependConstantInt32(BufferBuilderList & builder, LPCWSTR clue, int value, ConstantSizedBufferBuilderOf<int> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int32);
    }

    uint32 PrependConstantInt64(BufferBuilderList & builder, LPCWSTR clue, int64 value, ConstantSizedBufferBuilderOf<int64> ** entryOut = nullptr)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<int64>, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        if (entryOut)
        {
            *entryOut = entry;
        }
        return sizeof(int64);
    }

    uint32 PrependByte(BufferBuilderList & builder, LPCWSTR clue, byte value)
    {
        auto entry = Anew(alloc, BufferBuilderByte, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(byte);
    }

    uint32 PrependFunctionBodyFlags(BufferBuilderList & builder, LPCWSTR clue, FunctionBody::FunctionBodyFlags value)
    {
        return PrependByte(builder, clue, (byte) value);
    }

    uint32 PrependBool(BufferBuilderList & builder, LPCWSTR clue, bool value)
    {
        return PrependByte(builder, clue, (byte) value);
    }

    uint32 PrependFloat(BufferBuilderList & builder, LPCWSTR clue, float value)
    {
        auto entry = Anew(alloc, BufferBuilderFloat, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(float);
    }

    uint32 PrependDouble(BufferBuilderList & builder, LPCWSTR clue, double value)
    {
        auto entry = Anew(alloc, BufferBuilderDouble, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(double);
    }

    uint32 PrependSIMDValue(BufferBuilderList & builder, LPCWSTR clue, SIMDValue value)
    {
        auto entry = Anew(alloc, BufferBuilderSIMD, clue, value);
        builder.list = builder.list->Prepend(entry, alloc);
        return sizeof(SIMDValue);
    }

    uint32 PrependString16(__in BufferBuilderList & builder, __in_nz LPCWSTR clue, __in_bcount_opt(byteLength) LPCWSTR sz, __in uint32 byteLength)
    {
        if (sz != nullptr)
        {
            auto bb = Anew(alloc, ByteBuffer, byteLength, (void*)sz); // Includes trailing null
            return PrependInt32(builder, clue, GetString16Id(bb));
        }
        else
        {
            return PrependInt32(builder, clue, 0xffffffff);
        }
    }

    uint32 PrependByteBuffer(BufferBuilderList & builder, LPCWSTR clue, ByteBuffer * bb)
    {
        auto id = GetString16Id(bb);
        return PrependInt32(builder, clue, id);
    }

    int GetIdOfString(__in_bcount_opt(byteLength) LPCWSTR sz, __in uint32 byteLength)
    {
        auto bb = Anew(alloc, ByteBuffer, byteLength, (void*)sz); // Includes trailing null
        return GetString16Id(bb);
    }

    int GetIdOfPropertyRecord(const PropertyRecord * propertyRecord)
    {
        AssertMsg(!propertyRecord->IsSymbol(), "bytecode serializer does not currently handle non-built-in symbol PropertyRecords");
        size_t byteCount = ((size_t)propertyRecord->GetLength() + 1) * sizeof(char16);
        if (byteCount > UINT_MAX)
        {
            // We should never see property record that big
            Js::Throw::InternalError();
        }
        auto buffer = propertyRecord->GetBuffer();
#if DBG
        const PropertyRecord * propertyRecordCheck;
        scriptContext->FindPropertyRecord(buffer, propertyRecord->GetLength(), &propertyRecordCheck);
        Assert(propertyRecordCheck == propertyRecord);
#endif
        auto bb = Anew(alloc, ByteBuffer, (uint32)byteCount, (void*)buffer);
        return GetString16Id(bb, /*isPropertyRecord=*/ true);
    }

    template<typename TLayout>
    unaligned TLayout * DuplicateLayout(unaligned const TLayout * in)
    {
        auto sizeOfLayout = sizeof(unaligned TLayout);
        auto newLayout = AnewArray(alloc, byte, sizeOfLayout);
        js_memcpy_s(newLayout, sizeOfLayout, in, sizeOfLayout);
        return (unaligned TLayout * )newLayout;
    }

    template<typename T>
    uint32 Prepend(BufferBuilderList & builder, LPCWSTR clue, T * t)
    {
        auto block = Anew(alloc, BufferBuilderRaw, clue, sizeof(serialization_alignment T), (const byte*)t);
        builder.list = builder.list->Prepend(block, alloc);
        return sizeof(serialization_alignment T);
    }

    struct AuxRecord
    {
        SerializedAuxiliaryKind kind;
        uint offset;
    };

#ifdef ASMJS_PLAT
    HRESULT RewriteAsmJsByteCodesInto(BufferBuilderList & builder, LPCWSTR clue, FunctionBody * function, ByteBlock * byteBlock, SerializedFieldList& definedFields)
    {
        SListCounted<AuxRecord> auxRecords(alloc);

        auto finalSize = Anew(alloc, BufferBuilderInt32, _u("Final Byte Code Size"), 0); // Initially set to zero
        builder.list = builder.list->Prepend(finalSize, alloc);

        ByteCodeReader reader;
        reader.Create(function);

        uint32 size = 0;
        const byte * opStart = nullptr;
        bool cantGenerate = false;

        auto saveBlock = [&]() {
            uint32 byteCount;
            if (TryConvertToUInt32(reader.GetIP() - opStart, &byteCount))
            {
                if (!GenerateByteCodeForNative())
                {
                    auto block = Anew(alloc, BufferBuilderRaw, clue, byteCount, (const byte*)opStart);
                    builder.list = builder.list->Prepend(block, alloc);
                    size += byteCount;
                }
            }
            else
            {
                AssertMsg(false, "Unlikely: byte code size overflows 32 bits");
                cantGenerate = true;
            }
        };

        Assert(!function->HasCachedScopePropIds());

        while (!cantGenerate)
        {
            opStart = reader.GetIP();
            opStart; // For prefast. It can't figure out that opStart is captured in saveBlock above.
            LayoutSize layoutSize;
            OpCodeAsmJs op = reader.ReadAsmJsOp(layoutSize);
            if (op == OpCodeAsmJs::EndOfBlock)
            {
                saveBlock();
                break;
            }

            OpLayoutTypeAsmJs layoutType = OpCodeUtilAsmJs::GetOpCodeLayout(op);
            switch (layoutType)
            {

#define LAYOUT_TYPE(layout) \
    case OpLayoutTypeAsmJs::##layout: { \
        Assert(layoutSize == SmallLayout); \
        reader.##layout(); \
        saveBlock(); \
        break; }
#define LAYOUT_TYPE_WMS(layout) \
    case OpLayoutTypeAsmJs::##layout: { \
        switch (layoutSize) \
        { \
        case SmallLayout: \
            reader.##layout##_Small(); \
            break; \
        case MediumLayout: \
            reader.##layout##_Medium(); \
            break; \
        case LargeLayout: \
            reader.##layout##_Large(); \
            break; \
        default: \
            Assume(UNREACHED); \
        } \
       saveBlock(); \
       break;     }
#include "LayoutTypesAsmJs.h"
            default:
                AssertMsg(false, "Unknown OpLayout");
                cantGenerate = true;
                break;
            }
        }

        if (cantGenerate)
        {
            return ByteCodeSerializer::CantGenerate;
        }

        if (size != byteBlock->GetLength() && !GenerateByteCodeForNative())
        {
            Assert(size == byteBlock->GetLength());
            return ByteCodeSerializer::CantGenerate;
        }
        finalSize->value = size;

        RewriteAuxiliaryInto(builder, auxRecords, reader, function, definedFields);
        return S_OK;
    }
#endif

    HRESULT RewriteByteCodesInto(BufferBuilderList & builder, LPCWSTR clue, FunctionBody * function, ByteBlock * byteBlock, SerializedFieldList& definedFields)
    {
        SListCounted<AuxRecord> auxRecords(alloc);

        auto finalSize = Anew(alloc, BufferBuilderInt32, _u("Final Byte Code Size"), 0); // Initially set to zero
        builder.list = builder.list->Prepend(finalSize, alloc);

        ByteCodeReader reader;
        reader.Create(function);

        uint32 size = 0;
        const byte * opStart = nullptr;
        bool cantGenerate = false;

        auto saveBlock = [&]() {
            uint32 byteCount;
            if (TryConvertToUInt32(reader.GetIP()-opStart, &byteCount))
            {
                if (!GenerateByteCodeForNative())
                {
                    auto block = Anew(alloc, BufferBuilderRaw, clue, byteCount, (const byte*) opStart);
                    builder.list = builder.list->Prepend(block, alloc);
                    size += byteCount;
                }
            }
            else
            {
                AssertMsg(false, "Unlikely: byte code size overflows 32 bits");
                cantGenerate = true;
            }
        };

        while(!cantGenerate)
        {
            opStart = reader.GetIP();
            opStart; // For prefast. It can't figure out that opStart is captured in saveBlock above.
            LayoutSize layoutSize;
            OpCode op = reader.ReadOp(layoutSize);
            if (op == OpCode::EndOfBlock)
            {
                saveBlock();
                break;
            }

            OpLayoutType layoutType = OpCodeUtil::GetOpCodeLayout(op);
            switch (layoutType)
            {

#define DEFAULT_LAYOUT(op) \
    case OpLayoutType::##op: { \
        Assert(layoutSize == SmallLayout); \
        reader.##op(); \
        saveBlock(); \
        break; }
#define DEFAULT_LAYOUT_WITH_ONEBYTE(op) \
    case OpLayoutType::##op: { \
        switch (layoutSize) \
        { \
        case SmallLayout: \
            reader.##op##_Small(); \
            break; \
        case MediumLayout: \
            reader.##op##_Medium(); \
            break; \
        case LargeLayout: \
            reader.##op##_Large(); \
            break; \
        default: \
            Assert(false); \
            __assume(false); \
        } \
       saveBlock(); \
       break;     }
#define DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(op) \
        DEFAULT_LAYOUT_WITH_ONEBYTE(op); \
        DEFAULT_LAYOUT_WITH_ONEBYTE(Profiled##op)

                DEFAULT_LAYOUT(Empty);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg2U);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg3);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg3U);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg4);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg4U);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg5);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg5U);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg3C);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Arg);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ArgNoSrc);
                DEFAULT_LAYOUT(Br);
#ifdef BYTECODE_BRANCH_ISLAND
                DEFAULT_LAYOUT(BrLong);
#endif
                DEFAULT_LAYOUT(BrS);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg1Unsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(BrReg3);
                DEFAULT_LAYOUT(StartCall);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Profiled2CallI);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallI);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIFlags);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIWithICIndex);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(CallIFlagsWithICIndex);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementI);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementUnsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlot);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlotI1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlotI2);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementSlotI3);
                DEFAULT_LAYOUT(W1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(Reg1Unsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg2Int1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Unsigned1);
                DEFAULT_LAYOUT_WITH_ONEBYTE_AND_PROFILED(ElementCP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementRootCP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementP);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementPIndexed);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg2B1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(Reg3B1);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementU);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementRootU);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementScopedC);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementScopedC2);
                DEFAULT_LAYOUT(BrProperty);
                DEFAULT_LAYOUT(BrEnvProperty);
                DEFAULT_LAYOUT(BrLocalProperty);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementC2);
                DEFAULT_LAYOUT_WITH_ONEBYTE(ElementC);

#undef DEFAULT_LAYOUT
#undef DEFAULT_LAYOUT_WITH_ONEBYTE
                case OpLayoutType::AuxNoReg:
                    switch (op)
                    {
                        case OpCode::InitCachedFuncs:
                        {
                            auto layout = reader.AuxNoReg();
                            AuxRecord record = { sakFuncInfoArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::AuxNoReg");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::Auxiliary:
                    switch (op)
                    {
                        case OpCode::NewScObjectLiteral:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakPropertyIdArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::LdPropIds:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakPropertyIdArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::StArrSegItem_A:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakVarArrayIntCount, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScObject_A:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakVarArrayVarCount, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScIntArray:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::NewScFltArray:
                        {
                            auto layout = reader.Auxiliary();
                            AuxRecord record = { sakFloatArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::Auxiliary");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::ProfiledAuxiliary:
                    switch (op)
                    {
                        case OpCode::ProfiledNewScIntArray:
                        {
                            auto layout = reader.ProfiledAuxiliary();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        case OpCode::ProfiledNewScFltArray:
                        {
                            auto layout = reader.ProfiledAuxiliary();
                            AuxRecord record = { sakFloatArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::ProfiledAuxiliary");
                            cantGenerate = true;
                            break;
                    }
                    break;
                case OpLayoutType::Reg2Aux:
                    switch (op)
                    {
                        case OpCode::SpreadArrayLiteral:
                        {
                            auto layout = reader.Reg2Aux();
                            AuxRecord record = { sakIntArray, layout->Offset };
                            auxRecords.Prepend(record);
                            saveBlock();
                            break;
                        }
                        default:
                            AssertMsg(false, "Unknown OpCode for OpLayoutType::Reg2Aux");
                            cantGenerate = true;
                            break;
                    }
                    break;

#define STORE_SPREAD_AUX_ARGS \
                    if (!(layout->Options & CallIExtended_SpreadArgs)) \
                    { \
                        break; \
                    } \
                    AuxRecord record = { sakIntArray, layout->SpreadAuxOffset }; \
                    auxRecords.Prepend(record)

#define CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(op) \
                    case OpLayoutType::##op: \
                    { \
                        switch (layoutSize) \
                        { \
                        case SmallLayout: \
                        { \
                            auto layout = reader.##op##_Small(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        case MediumLayout: \
                        { \
                            auto layout = reader.##op##_Medium(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        case LargeLayout: \
                        { \
                            auto layout = reader.##op##_Large(); \
                            STORE_SPREAD_AUX_ARGS; \
                            break; \
                        } \
                        default: \
                            Assert(false); \
                            __assume(false); \
                        } \
                       saveBlock(); \
                       break; \
                    }

                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(CallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(CallIExtendedFlags)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedFlags)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(Profiled2CallIExtended)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedWithICIndex)
                    CALLIEXTENDED_LAYOUT_WITH_ONEBYTE(ProfiledCallIExtendedFlagsWithICIndex)

                default:
                    AssertMsg(false, "Unknown OpLayout");
                    cantGenerate = true;
                    break;
            }
        }

        if (cantGenerate)
        {
            return ByteCodeSerializer::CantGenerate;
        }

        if (size != byteBlock->GetLength() && !GenerateByteCodeForNative())
        {
            Assert(size == byteBlock->GetLength());
            return ByteCodeSerializer::CantGenerate;
        }
        finalSize->value = size;

        RewriteAuxiliaryInto(builder, auxRecords, reader, function, definedFields);
        return S_OK;
    }


    void RewriteAuxiliaryInto(BufferBuilderList& builder, SListCounted<AuxRecord> const& auxRecordList,
        ByteCodeReader& reader, FunctionBody * functionBody, SerializedFieldList& definedFields)
    {
        uint count = auxRecordList.Count();

        if (count == 0)
        {
            return;
        }

        definedFields.has_auxiliary = true;
        PrependInt32(builder, _u("Auxiliary Structure Count"), count);
        auto writeAuxVarArray = [&](uint offset, bool isVarCount, int count, const Var * elements)  {
            typedef serialization_alignment SerializedVarArray T;
            T header(offset, isVarCount, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("Var Array"), header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto var = elements[i];
                PrependVarConstant(builder, var);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("Magic end of aux var array"), magicEndOfAuxVarArray);
            PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
        };
        auto writeAuxVarArrayIntCount = [&](uint offset) {
            const AuxArray<Var> * varArray = reader.ReadAuxArray<Var>(offset, functionBody);
            int count = varArray->count;
            const Var * elements = varArray->elements;
            writeAuxVarArray(offset, false, count, elements);
        };
        auto writeAuxVarArrayVarCount = [&](uint offset) {
            const VarArrayVarCount * varArray = reader.ReadVarArrayVarCount(offset, functionBody);
            int count = Js::TaggedInt::ToInt32(varArray->count);
            const Var * elements = varArray->elements;
            writeAuxVarArray(offset, true, count, elements);
        };
        auto writeAuxIntArray = [&](uint offset) -> BufferBuilder* {
            const AuxArray<int32> *ints = reader.ReadAuxArray<int32>(offset, functionBody);
            int count = ints->count;
            const int32 * elements = ints->elements;

            typedef serialization_alignment SerializedIntArray T;
            T header(offset, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("Int Array"), header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto value = elements[i];
                PrependConstantInt32(builder, _u("Integer Constant Value"), value);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("Magic end of aux int array"), magicEndOfAuxIntArray);
            PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxFloatArray = [&](uint offset) -> BufferBuilder* {
            const AuxArray<double> *doubles = reader.ReadAuxArray<double>(offset, functionBody);
            int count = doubles->count;
            const double * elements = doubles->elements;

            typedef serialization_alignment SerializedFloatArray T;
            T header(offset, count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("Float Array"), header);
            builder.list = builder.list->Prepend(block, alloc);

            for (int i=0;i<count; i++)
            {
                auto value = elements[i];
                PrependDouble(builder, _u("Number Constant Value"), value);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("Magic end of aux float array"), magicEndOfAuxFltArray);
            PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxPropertyIdArray = [&](uint offset, byte extraSlots) -> BufferBuilder* {
            const PropertyIdArray * propIds = reader.ReadPropertyIdArray(offset, functionBody);

            typedef serialization_alignment SerializedPropertyIdArray T;
            T header(offset, propIds->count, extraSlots, propIds->hadDuplicates, propIds->has__proto__);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("Property Id Array"), header);
            builder.list = builder.list->Prepend(block, alloc);

            for (uint32 i=0; i<propIds->count; i++)
            {
                auto original = propIds->elements[i];
                auto encoded = encodePossiblyBuiltInPropertyId(original);
                PrependConstantInt32(builder, _u("Encoded Property Id"), encoded);
            }
            auto slots = propIds->elements + propIds->count;
            for(byte i=0; i<extraSlots; i++)
            {
                PrependConstantInt32(builder, _u("Extra Slot"), slots[i]);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("Magic end of aux section"), magicEndOfAuxPropIdArray);
            PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
            return block;
        };

        auto writeAuxFuncInfoArray = [&](uint offset) -> BufferBuilder* {
            const FuncInfoArray * funcInfos = reader.ReadAuxArray<FuncInfoEntry>(offset, functionBody);

            typedef serialization_alignment SerializedFuncInfoArray T;
            T header(offset, funcInfos->count);
            auto block = Anew(alloc, ConstantSizedBufferBuilderOf<T>, _u("Funcinfo Array"), header);
            builder.list = builder.list->Prepend(block, alloc);

            for (uint32 i=0; i<funcInfos->count; i++)
            {
                auto funcInfo = funcInfos->elements[i];
                PrependConstantInt32(builder, _u("FuncInfo nestedIndex"), funcInfo.nestedIndex);
                PrependConstantInt32(builder, _u("FuncInfo scopeSlot"), funcInfo.scopeSlot);
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("Magic end of aux section"), magicEndOfAuxFuncInfoArray);
            PrependInt32(builder, _u("Magic end of aux"), magicEndOfAux);
#endif
            return block;
        };

        PrependInt32(builder, _u("Auxiliary Size"),
            functionBody->GetAuxiliaryData()? functionBody->GetAuxiliaryData()->GetLength() : 0);
        PrependInt32(builder, _u("Auxiliary Context Size"),
            functionBody->GetAuxiliaryContextData()? functionBody->GetAuxiliaryContextData()->GetLength() : 0);
        auxRecordList.Map([&](AuxRecord const& auxRecord)
        {
            switch (auxRecord.kind)
            {
            default:
                AssertMsg(false, "Unexpected auxiliary kind");
                Throw::FatalInternalError();
                break;

            case sakVarArrayIntCount:
                writeAuxVarArrayIntCount(auxRecord.offset);
                break;

            case sakVarArrayVarCount:
                writeAuxVarArrayVarCount(auxRecord.offset);
                break;

            case sakIntArray:
                writeAuxIntArray(auxRecord.offset);
                break;

            case sakFloatArray:
                writeAuxFloatArray(auxRecord.offset);
                break;

            case sakPropertyIdArray:
                writeAuxPropertyIdArray(auxRecord.offset, 0);
                break;

            case sakFuncInfoArray:
                writeAuxFuncInfoArray(auxRecord.offset);
                break;
            };
        });
    }

    uint32 PrependStringConstant(BufferBuilderList & builder, Var var)
    {
        auto str = VarTo<JavascriptString>(var);
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start String Constant"), magicStartStringConstant);
#endif
        auto bb = Anew(alloc, ByteBuffer, (str->GetLength() + 1) * sizeof(char16), (void*)str->GetSz());
        size += PrependByteBuffer(builder, _u("String Constant 16 Value"), bb);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End String Constant"), magicEndStringConstant);
#endif

        return size;
    }

    uint32 PrependStringTemplateCallsiteConstant(BufferBuilderList & builder, Var var)
    {
        ES5Array* callsite = VarTo<ES5Array>(var);
        Var element = nullptr;
        auto size = PrependInt32(builder, _u("String Template Callsite Constant String Count"), (int)callsite->GetLength());

        for (uint32 i = 0; i < callsite->GetLength(); i++)
        {
            callsite->DirectGetItemAt(i, &element);
            size += PrependStringConstant(builder, element);
        }

        Var rawVar = JavascriptOperators::OP_GetProperty(callsite, Js::PropertyIds::raw, callsite->GetScriptContext());
        ES5Array* rawArray = VarTo<ES5Array>(rawVar);

        for (uint32 i = 0; i < rawArray->GetLength(); i++)
        {
            rawArray->DirectGetItemAt(i, &element);
            size += PrependStringConstant(builder, element);
        }

        return size;
    }

    uint32 PrependVarConstant(BufferBuilderList & builder, Var var)
    {
        if (var == (Js::Var)&Js::NullFrameDisplay)
        {
            return PrependByte(builder, _u("Null Frame Display"), ctNullDisplay);
        }
        else if (var == (Js::Var)&Js::StrictNullFrameDisplay)
        {
            return PrependByte(builder, _u("Strict Null Frame Display"), ctStrictNullDisplay);
        }

        auto typeId = JavascriptOperators::GetTypeId(var);
        switch (typeId)
        {
        case TypeIds_Undefined:
            return PrependByte(builder, _u("Undefined Constant"), ctUndefined);

        case TypeIds_Null:
            return PrependByte(builder, _u("Null Constant"), ctNull);

        case TypeIds_Boolean:
            return PrependByte(builder, _u("Boolean Constant"), VarTo<JavascriptBoolean>(var)->GetValue()? ctTrue : ctFalse);

        case TypeIds_Number:
        {
            auto size = PrependByte(builder, _u("Number Constant"), ctNumber);
            return size + PrependDouble(builder,  _u("Number Constant Value"), JavascriptNumber::GetValue(var));
        }

        case TypeIds_Integer:
        {
            int32 value = TaggedInt::ToInt32(var);
            if ((int8)value == value)
            {
                auto size = PrependByte(builder, _u("Integer Constant"), ctInt8);
                return size + PrependByte(builder, _u("Integer Constant Value"), (byte)value);
            }
            else if ((int16)value == value)
            {
                auto size = PrependByte(builder, _u("Integer Constant"), ctInt16);
                return size + PrependConstantInt16(builder, _u("Integer Constant Value"), (int16)value);
            }
            else
            {
                auto size = PrependByte(builder, _u("Integer Constant"), ctInt32);
                return size + PrependConstantInt32(builder, _u("Integer Constant Value"), value);
            }
        }

        case TypeIds_String:
        {
            auto size = PrependByte(builder, _u("String Constant 16"),
                Js::VarIs<Js::PropertyString>(var)? ctPropertyString16 : ctString16);
            return size + PrependStringConstant(builder, var);
        }

        case TypeIds_ES5Array:
        {
            // ES5Array objects in the constant table are always string template callsite objects.
            // If we later put other ES5Array objects in the constant table, we'll need another way
            // to decide the constant type.
            auto size = PrependByte(builder, _u("String Template Callsite Constant"), ctStringTemplateCallsite);
            return size + PrependStringTemplateCallsiteConstant(builder, var);
        }

        default:
            AssertMsg(UNREACHED, "Unexpected object type in AddConstantTable");
            Throw::FatalInternalError();
        }
    }

#ifdef ASMJS_PLAT
    uint32 AddAsmJsConstantTable(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start Constant Table"), magicStartOfConstantTable);
#endif

        auto constTable = function->GetConstTable();
        byte* tableEnd = (byte*)(constTable + function->GetConstantCount());

        for (int i = 0; i < WAsmJs::LIMIT; ++i)
        {
            WAsmJs::Types type = (WAsmJs::Types)i;
            WAsmJs::TypedSlotInfo* typedInfo = function->GetAsmJsFunctionInfo()->GetTypedSlotInfo(type);
            uint32 constCount = typedInfo->constCount;
            if (constCount > FunctionBody::FirstRegSlot)
            {
                uint32 typeSize = WAsmJs::GetTypeByteSize(type);
                byte* byteTable = ((byte*)constTable) + typedInfo->constSrcByteOffset;
                byteTable += typeSize * FunctionBody::FirstRegSlot;
                for (uint32 reg = FunctionBody::FirstRegSlot; reg < constCount; ++reg)
                {
                    switch (type)
                    {
                    case WAsmJs::INT32:   PrependConstantInt32(builder, _u("Integer Constant Value"), *(int*)byteTable); break;
                    case WAsmJs::FLOAT32: PrependFloat(builder, _u("Float Constant Value"), *(float*)byteTable); break;
                    case WAsmJs::FLOAT64: PrependDouble(builder, _u("Double Constant Value"), *(double*)byteTable); break;
                    default:
                        Assert(UNREACHED);
                        Js::Throw::FatalInternalError();
                        break;
                    }
                    byteTable += typeSize;
                }

                if (byteTable > tableEnd)
                {
                    Assert(UNREACHED);
                    Js::Throw::FatalInternalError();
                }
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End Constant Table"), magicEndOfConstantTable);
#endif

        return size;
    }
#endif

    uint32 AddConstantTable(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start Constant Table"), magicStartOfConstantTable);
#endif

        for (auto reg = FunctionBody::FirstRegSlot + 1; reg < function->GetConstantCount(); reg++) // Ignore first slot, it is always global object or module root object
        {
            auto var = function->GetConstantVar(reg);
            Assert(var != nullptr);
            size += PrependVarConstant(builder, var);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End Constant Table"), magicEndOfConstantTable);
#endif

        return size;
    }

    uint32 AddPropertyIdsForScopeSlotArray(BufferBuilderList & builder, ParseableFunctionInfo* function)
    {
        if (function->scopeSlotArraySize == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start PropertyIdsForScopeSlotsArray"), magicStartOfPropertyIdsForScopeSlotArray);
#endif

        for (uint i = 0; i < function->scopeSlotArraySize; i++)
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdsForScopeSlotArray()[i]);
            size += PrependInt32(builder, _u("PropertyIdsForScopeSlots"), propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End PropertyIdsForScopeSlotsArray"), magicEndOfPropertyIdsForScopeSlotArray);
#endif

        return size;
    }

    uint32 AddSlotIdInCachedScopeToNestedIndexArray(BufferBuilderList& builder, FunctionBody * functionBody)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start SlotIdInCachedScopeToNestedIndexArray"), magicStartOfSlotIdToNestedIndexArray);
#endif

        Js::AuxArray<uint32> * slotIdToNestedIndexArray = functionBody->GetSlotIdInCachedScopeToNestedIndexArray();
        size += PrependInt32(builder, _u("SlotIdInCachedScopeToNestedIndexArray count"), slotIdToNestedIndexArray->count);
        for (uint i = 0; i < slotIdToNestedIndexArray->count; i++)
        {
            size += PrependInt32(builder, _u("Nested function index for slot id in cached scope"), slotIdToNestedIndexArray->elements[i]);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End magicStartOfSlotIdToNestedIndexArray"), magicEndOfSlotIdToNestedIndexArray);
#endif
        return size;
    }

#if ENABLE_NATIVE_CODEGEN
    uint32 AddCallSiteToCallApplyCallSiteArray(BufferBuilderList& builder, FunctionBody * functionBody)
    {
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start CallSiteToCallApplyCallSiteArray"), magicStartOfCallSiteToCallApplyCallSiteArray);
#endif
        Js::ProfileId * callSiteToCallApplyCallSiteArray = functionBody->GetCallSiteToCallApplyCallSiteArray();
        for (Js::ProfileId i = 0; i < functionBody->GetProfiledCallSiteCount(); i++)
        {
            size += PrependInt16(builder, _u(".call/.apply call site id for call site id"), callSiteToCallApplyCallSiteArray[i]);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End CallSiteToCallApplyCallSiteArray"), magicEndOfCallSiteToCallApplyCallSiteArray);
#endif
        return size;
    }
#endif

    // Gets the number of debugger slot array scopes there are in the function body's scope chain list.
    uint32 GetDebuggerScopeSlotArrayCount(FunctionBody * function)
    {
        Assert(function);
        uint debuggerScopeSlotArraySize = 0;
        if (function->GetScopeObjectChain())
        {
            debuggerScopeSlotArraySize = function->GetScopeObjectChain()->pScopeChain->CountWhere([&](DebuggerScope* scope)
            {
                return scope->IsSlotScope();
            });
        }

        return debuggerScopeSlotArraySize;
    }

    // Adds the debugger scopes that are slot array type to the serialized bytecode.
    // This is to ensure that block scope slot array properties are captured along with
    // function level slot array properties.
    uint32 AddSlotArrayDebuggerScopeProperties(BufferBuilderList & builder, DebuggerScope* debuggerScope, uint propertiesCount)
    {
        Assert(debuggerScope);
        if (propertiesCount == 0)
        {
            return 0u;
        }

        uint32 size = 0u;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start SlotArrayDebuggerScopeProperties"), magicStartOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        AssertMsg(debuggerScope->HasProperties(), "Properties should exist.");
        Assert(debuggerScope->scopeProperties->Count() >= 0);
        AssertMsg((uint)debuggerScope->scopeProperties->Count() == propertiesCount, "Property counts should match.");
        for (uint i = 0u; i < propertiesCount; ++i)
        {
            DebuggerScopeProperty scopeProperty = debuggerScope->scopeProperties->Item(i);
            size += PrependInt32(builder, _u("SlotIndexesForDebuggerScopeSlots"), scopeProperty.location);

            PropertyId propertyId = encodePossiblyBuiltInPropertyId(scopeProperty.propId);
            size += PrependInt32(builder, _u("PropertyIdsForDebuggerScopeSlots"), propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End SlotArrayDebuggerScopeProperties"), magicEndOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return size;
    }

    // Adds the debugger scopes that are slot array type to the serialized bytecode.
    // This is to ensure that block scope slot array properties are captured along with
    // function level slot array properties.
    uint32 AddSlotArrayDebuggerScopes(BufferBuilderList & builder, FunctionBody* function, uint debuggerScopeSlotArraySize)
    {
        Assert(function);
        if (function->GetScopeObjectChain() == nullptr || debuggerScopeSlotArraySize == 0)
        {
            return 0u;
        }

        uint32 size = 0u;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start SlotArrayDebuggerScopes"), magicStartOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        uint slotArrayCount = 0;
        for (uint i = 0u; i < static_cast<uint>(function->GetScopeObjectChain()->pScopeChain->Count()); ++i)
        {
            DebuggerScope* debuggerScope = function->GetScopeObjectChain()->pScopeChain->Item(i);
            if (debuggerScope->IsSlotScope())
            {
                // Only add slot scope debugger scopes (store the index of the scope).
                size += PrependInt32(builder, _u("SlotArrayDebuggerScope"), i);

                // Store the count of properties for the scope.
                int propertiesCount = debuggerScope->HasProperties() ? debuggerScope->scopeProperties->Count() : 0u;
                size += PrependInt32(builder, _u("Debugger Scope Slot Array Property Count"), propertiesCount);
                size += AddSlotArrayDebuggerScopeProperties(builder, debuggerScope, propertiesCount);
                slotArrayCount++;
            }
        }
        Assert(debuggerScopeSlotArraySize == slotArrayCount);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End SlotArrayDebuggerScopes"), magicEndOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return size;
    }

    uint32 AddCacheIdToPropertyIdMap(BufferBuilderList & builder, FunctionBody * function)
    {
        uint count = function->GetInlineCacheCount();
        if (count == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, _u("Start CacheId-to-PropertyId map"), magicStartOfCacheIdToPropIdMap);
#endif

        for (uint i = 0; i < count; i++)
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(function->GetPropertyIdFromCacheId(i));
            size += PrependInt32(builder, _u("CacheIdToPropertyId"), propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End CacheId-to-PropertyId map"), magicEndOfCacheIdToPropIdMap);
#endif

        return size;
    }

    uint32 AddReferencedPropertyIdMap(BufferBuilderList & builder, FunctionBody * function)
    {
        uint count = function->GetReferencedPropertyIdCount();
        if (count == 0)
        {
            return 0;
        }
        uint32 size = 0;

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, _u("Start Referenced-PropertyId map"), magicStartOfReferencedPropIdMap);
#endif

        for (uint i = 0; i < count; i++)
        {
            PropertyId propertyId = encodeNonBuiltinPropertyId(function->GetReferencedPropertyIdWithMapIndex(i));
            size += PrependInt32(builder, _u("ReferencedPropertyId"), propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End Referenced-PropertyId map"), magicEndOfReferencedPropIdMap);
#endif

        return size;
    }

    uint32 PrependByteArray(BufferBuilderList & builder, int length, byte * buffer)
    {
        int size = 0;
        for (int i = 0; i<length; ++i)
        {
            size += PrependByte(builder, _u("Byte Array Element"), buffer[i]);
        }
        return size;
    }

    uint32 PrependUInt32Array(BufferBuilderList & builder, int length, uint32 * buffer)
    {
        int size = 0;
        for(int i=0;i<length;++i)
        {
            size += PrependConstantInt32(builder, _u("UInt32 Array Element"), buffer[i]);
        }
        return size;
    }

    uint32 PrependGrowingUint32Array(BufferBuilderList & builder, LPCWSTR clue, JsUtil::GrowingUint32HeapArray * arr)
    {
        if (arr == nullptr || arr->Count() == 0 || arr->GetLength() == 0 || arr->GetBuffer() == nullptr)
        {
            return PrependInt32(builder, clue, 0);
        }
        auto size = PrependInt32(builder, clue, arr->Count());
        size += PrependUInt32Array(builder, arr->Count(), arr->GetBuffer());
        return size;
    }

    uint32 PrependSmallSpanSequence(BufferBuilderList & builder, LPCWSTR clue, SmallSpanSequence * spanSequence)
    {
        auto size = PrependInt32(builder, clue, spanSequence->baseValue);
        size += PrependGrowingUint32Array(builder, _u("Statement Buffer"), spanSequence->pStatementBuffer);
        size += PrependGrowingUint32Array(builder, _u("Actual Offset List"), spanSequence->pActualOffsetList);
        return size;
    }

    template <typename TStructType>
    uint32 PrependStruct(BufferBuilderList & builder, LPCWSTR clue, TStructType * value)
    {
        auto entry = Anew(alloc, ConstantSizedBufferBuilderOf<TStructType>, clue, *value);
        builder.list = builder.list->Prepend(entry, alloc);

        return sizeof(serialization_alignment TStructType);
    }

    uint32 AddPropertyIdOfFormals(BufferBuilderList & builder, PropertyIdArray * propIds, FunctionBody * function)
    {
        uint32 size = 0;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("Start propertyids of formals"), magicStartOfPropIdsOfFormals);
#endif

        byte extraSlotCount = 0;
        if (function->HasCachedScopePropIds())
        {
            extraSlotCount = ActivationObjectEx::ExtraSlotCount();
        }

        size += PrependInt32(builder, _u("ExportsIdArrayLength"), propIds->count);
        size += PrependByte(builder, _u("ExtraSlotsCount"), extraSlotCount);
        size += PrependByte(builder, _u("ExportsIdArrayDups"), propIds->hadDuplicates);
        size += PrependByte(builder, _u("ExportsIdArray__proto__"), propIds->has__proto__);
        size += PrependByte(builder, _u("ExportsIdArrayHasNonSimpleParams"), propIds->hasNonSimpleParams);

        for (uint i = 0; i < propIds->count; i++)
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(propIds->elements[i]);
            size += PrependInt32(builder, _u("ExportsIdArrayElem"), propertyId);
        }

        auto slots = propIds->elements + propIds->count;
        for (byte i = 0; i < extraSlotCount; i++)
        {
            size += PrependInt32(builder, _u("Extra Slot"), slots[i]);
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End of prop ids for formals array"), magicEndOfPropIdsOfFormals);
#endif
        return size;
    }

#ifdef ASMJS_PLAT
    uint32 AddAsmJsFunctionInfo(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;
        AsmJsFunctionInfo* funcInfo = function->GetAsmJsFunctionInfo();

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, _u("Start Asm.js Function Info"), magicStartOfAsmJsFuncInfo);
#endif
        size += PrependInt32(builder, _u("ReturnType"), funcInfo->GetReturnType().which());
        size += PrependInt16(builder, _u("ArgCount"), funcInfo->GetArgCount());
        size += PrependInt16(builder, _u("ArgSize"), funcInfo->GetArgByteSize());
        size += PrependInt16(builder, _u("ArgSizeArrayLength"), funcInfo->GetArgSizeArrayLength());
        size += PrependUInt32Array(builder, funcInfo->GetArgSizeArrayLength(), funcInfo->GetArgsSizesArray());
        size += PrependByteArray(builder, funcInfo->GetArgCount(), (byte*)funcInfo->GetArgTypeArray());
        size += PrependByte(builder, _u("UsesHeapBuffer"), funcInfo->UsesHeapBuffer());
        for (int i = WAsmJs::LIMIT - 1; i >= 0; --i)
        {
            const char16* clue = nullptr;
            switch (i)
            {
            case WAsmJs::INT32:   clue = _u("Int32TypedSlots"); break;
            case WAsmJs::INT64:   clue = _u("Int64TypedSlots"); break;
            case WAsmJs::FLOAT32: clue = _u("Float32TypedSlots"); break;
            case WAsmJs::FLOAT64: clue = _u("Float64TypedSlots"); break;
            case WAsmJs::SIMD:    clue = _u("SimdTypedSlots"); break;
            default:
                CompileAssert(WAsmJs::SIMD == WAsmJs::LastType);
                Assert(false);
                break;
            }
            size += PrependStruct<WAsmJs::TypedSlotInfo>(builder, clue, funcInfo->GetTypedSlotInfo((WAsmJs::Types)i));
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End Asm.js Function Info"), magicEndOfAsmJsFuncInfo);
#endif

        return size;
    }

    uint32 AddAsmJsModuleInfo(BufferBuilderList & builder, FunctionBody * function)
    {
        uint32 size = 0;
        AsmJsModuleInfo * moduleInfo = function->GetAsmJsModuleInfo();

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, _u("Start Asm.js Module Info"), magicStartOfAsmJsModuleInfo);
#endif

        size += PrependInt32(builder, _u("ArgInCount"), moduleInfo->GetArgInCount());
        size += PrependInt32(builder, _u("ExportsCount"), moduleInfo->GetExportsCount());
        size += PrependInt32(builder, _u("SlotsCount"), moduleInfo->GetSlotsCount());

        if (moduleInfo->GetExportsCount() > 0)
        {
            PropertyIdArray * propArray = moduleInfo->GetExportsIdArray();
            size += PrependByte(builder, _u("ExtraSlotsCount"), propArray->extraSlots);
            size += PrependByte(builder, _u("ExportsIdArrayDups"), propArray->hadDuplicates);
            size += PrependByte(builder, _u("ExportsIdArray__proto__"), propArray->has__proto__);
            size += PrependInt32(builder, _u("ExportsIdArrayLength"), propArray->count);
            for (uint i = 0; i < propArray->count; i++)
            {
                PropertyId propertyId = encodePossiblyBuiltInPropertyId(propArray->elements[i]);
                size += PrependInt32(builder, _u("ExportsIdArrayElem"), propertyId);
            }
            size += PrependUInt32Array(builder, moduleInfo->GetExportsCount(), moduleInfo->GetExportsFunctionLocation());
        }

        size += PrependInt32(builder, _u("ExportFunctionIndex"), moduleInfo->GetExportFunctionIndex());

        size += PrependInt32(builder, _u("VarCount"), moduleInfo->GetVarCount());
        for (int i = 0; i < moduleInfo->GetVarCount(); i++)
        {
            size += PrependStruct(builder, _u("ModuleVar"), &moduleInfo->GetVar(i));
        }

        size += PrependInt32(builder, _u("VarImportCount"), moduleInfo->GetVarImportCount());
        for (int i = 0; i < moduleInfo->GetVarImportCount(); i++)
        {
            auto import = moduleInfo->GetVarImport(i);
            size += PrependInt32(builder, _u("ImportLocation"), import.location);
            size += PrependByte(builder, _u("ImportType"), import.type);
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
            size += PrependInt32(builder, _u("ImportId"), propertyId);
        }

        size += PrependInt32(builder, _u("FunctionImportCount"), moduleInfo->GetFunctionImportCount());
        for (int i = 0; i < moduleInfo->GetFunctionImportCount(); i++)
        {
            auto import = moduleInfo->GetFunctionImport(i);
            size += PrependInt32(builder, _u("ImportLocation"), import.location);
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(import.field);
            size += PrependInt32(builder, _u("ImportId"), propertyId);
        }

        size += PrependInt32(builder, _u("FunctionCount"), moduleInfo->GetFunctionCount());
        for (int i = 0; i < moduleInfo->GetFunctionCount(); i++)
        {
            auto func = moduleInfo->GetFunction(i);
            size += PrependInt32(builder, _u("FuncLocation"), func.location);
        }

        size += PrependInt32(builder, _u("FunctionTableCount"), moduleInfo->GetFunctionTableCount());
        for (int i = 0; i < moduleInfo->GetFunctionTableCount(); i++)
        {
            auto table = moduleInfo->GetFunctionTable(i);
            size += PrependInt32(builder, _u("FuncTableSize"), table.size);
            PrependUInt32Array(builder, table.size, table.moduleFunctionIndex);
        }

        size += PrependStruct<AsmJsModuleMemory>(builder, _u("ModuleMemory"), &moduleInfo->GetModuleMemory());

        size += PrependInt32(builder, _u("AsmJsSlotMapCount"), moduleInfo->GetAsmJsSlotMap()->Count());
        auto slotIter = moduleInfo->GetAsmJsSlotMap()->GetIterator();
        while (slotIter.IsValid())
        {
            PropertyId propertyId = encodePossiblyBuiltInPropertyId(slotIter.CurrentKey());
            size += PrependInt32(builder, _u("AsmJsSlotPropId"), propertyId);
            size += PrependStruct(builder, _u("AsmJsSlotValue"), slotIter.CurrentValue());
            slotIter.MoveNext();
        }
        size += PrependStruct(builder, _u("MathBuiltinBV"), &moduleInfo->GetAsmMathBuiltinUsed());
        size += PrependStruct(builder, _u("ArrayBuiltinBV"), &moduleInfo->GetAsmArrayBuiltinUsed());

        size += PrependInt32(builder, _u("MaxHeapAccess"), moduleInfo->GetMaxHeapAccess());

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        size += PrependInt32(builder, _u("End Asm.js Module Info"), magicEndOfAsmJsModuleInfo);
#endif
        return size;
    }
#endif

    HRESULT AddFunctionBody(BufferBuilderList &builder, FunctionBody *function, SRCINFO const *srcInfo, SerializedFieldList& definedFields)
    {
        Assert(!function->GetIsSerialized());
        DebugOnly(function->SetIsSerialized(true));

        // This field should always be non-zero
        Assert(function->GetConstantCount() != 0);

#define PrependArgSlot PrependInt16
#define PrependRegSlot PrependInt32
#define PrependCharCount PrependInt32
#define PrependULong PrependInt32
#define PrependUInt16 PrependInt16
#define PrependUInt32 PrependInt32

        {
#define DEFINE_FUNCTION_BODY_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
            if (function->##name != 0) { \
                definedFields.has_##name = true; \
                Prepend##serializableType(builder, _u(#name), function->##name); \
            }

#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD_NO_CHECK(type, name, serializableType) \
            Prepend##serializableType(builder, _u(#name), function->Get##name##());

#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType, defaultValue) \
            if (function->Get##name##() != defaultValue) { \
                definedFields.has_##name = true; \
                DECLARE_SERIALIZABLE_ACCESSOR_FIELD_NO_CHECK(type, name, serializableType); \
            }

#include "SerializableFunctionFields.h"
        }

        {
            auto loopHeaderArray = function->GetLoopHeaderArray();
            if (loopHeaderArray != nullptr)
            {
                definedFields.has_loopHeaderArray = true;
                uint loopCount = function->GetLoopCount();
                for (uint i = 0; i < loopCount; ++i)
                {
                    PrependInt32(builder, _u("Loop Header Start"), loopHeaderArray[i].startOffset);
                    PrependInt32(builder, _u("Loop Header End"), loopHeaderArray[i].endOffset);
                }
            }

#ifdef ASMJS_PLAT
            if (function->GetAsmJsFunctionInfo())
            {
                definedFields.has_asmJsInfo = true;
                PrependByte(builder, _u("Asm.js Info Kind"), 1);
                AddAsmJsFunctionInfo(builder, function);
            }
            else if (function->GetIsAsmjsMode())
            {
                definedFields.has_asmJsInfo = true;
                PrependByte(builder, _u("Asm.js Info Kind"), 2);
                AddAsmJsModuleInfo(builder, function);
            }
#endif

#ifdef ASMJS_PLAT
            if (function->GetIsAsmJsFunction())
            {
                AddAsmJsConstantTable(builder, function);
                auto hr = RewriteAsmJsByteCodesInto(builder, _u("Rewritten Asm.js Byte Code"), function, function->byteCodeBlock, definedFields);
                if (FAILED(hr))
                {
                    return hr;
                }
            }
            else
#endif
            {
                AddConstantTable(builder, function);
                auto hr = RewriteByteCodesInto(builder, _u("Rewritten Byte Code"), function, function->byteCodeBlock, definedFields);
                if (FAILED(hr))
                {
                    return hr;
                }
            }

            PropertyIdArray * propIds = function->GetFormalsPropIdArray(false);
            if (propIds != nullptr)
            {
                definedFields.has_propertyIdOfFormals = true;
                AddPropertyIdOfFormals(builder, propIds, function);
            }

            AddCacheIdToPropertyIdMap(builder, function);
            AddReferencedPropertyIdMap(builder, function);

            if (function->GetSlotIdInCachedScopeToNestedIndexArray() == nullptr)
            {
                definedFields.has_slotIdInCachedScopeToNestedIndexArray = false;
            }
            else
            {
                definedFields.has_slotIdInCachedScopeToNestedIndexArray = true;
                AddSlotIdInCachedScopeToNestedIndexArray(builder, function);
            }

#if ENABLE_NATIVE_CODEGEN
            if (function->GetCallSiteToCallApplyCallSiteArray() == nullptr)
            {
                definedFields.has_callSiteToCallApplyCallSiteArray = false;
            }
            else
            {
                definedFields.has_callSiteToCallApplyCallSiteArray = true;
                AddCallSiteToCallApplyCallSiteArray(builder, function);
            }
#else
            definedFields.has_callSiteToCallApplyCallSiteArray = false;
#endif

            uint debuggerScopeSlotArraySize = GetDebuggerScopeSlotArrayCount(function);
            if (debuggerScopeSlotArraySize != 0)
            {
                definedFields.has_debuggerScopeSlotArray = true;
                PrependInt32(builder, _u("Debugger Scope Slot Array Size"), debuggerScopeSlotArraySize);
                AddSlotArrayDebuggerScopes(builder, function, debuggerScopeSlotArraySize);
            }

            // Literal regexes
            for (uint i = 0; i < function->GetLiteralRegexCount(); ++i)
            {
                const auto literalRegex = function->GetLiteralRegex(i);
                if (!literalRegex)
                {
                    PrependInt32(builder, _u("Literal regex source length"), -1);
                    continue;
                }

                const auto source = literalRegex->GetSource();
                PrependInt32(builder, _u("Literal regex source length"), source.GetLength());
                PrependString16(builder, _u("Literal regex source"), source.GetBuffer(), (source.GetLength() + 1) * sizeof(char16));
                PrependByte(builder, _u("Literal regex flags"), literalRegex->GetFlags());
            }

            // Write the SourceInfo stuff
            PrependSmallSpanSequence(builder, _u("Span Sequence"), function->m_sourceInfo.pSpanSequence);
        }

        return S_OK;
    }

    HRESULT AddFunction(BufferBuilderList & builder, ParseableFunctionInfo * function, SRCINFO const * srcInfo, ByteCodeCache* cache)
    {
        SerializedFieldList definedFields = { 0 };

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        PrependInt32(builder, _u("Start Function Table"), magicStartOfFunctionBody);
#endif

        uint32 sourceDiff = 0;

        if (!TryConvertToUInt32(function->StartOffset(), &sourceDiff))
        {
            Assert(0); // Likely a bug
            return ByteCodeSerializer::CantGenerate;
        }

        if (function->m_lineNumber != 0)
        {
            definedFields.has_m_lineNumber = true;
            PrependInt32(builder, _u("Line Number"), function->m_lineNumber);
        }

        if (function->m_columnNumber != 0)
        {
            definedFields.has_m_columnNumber = true;
            PrependInt32(builder, _u("Column Number"), function->m_columnNumber);
        }

        bool isAnonymous = function->GetIsAnonymousFunction();

        // FunctionBody Details
        DWORD bitFlags =
            (function->m_isDeclaration ? ffIsDeclaration : 0)
            | (function->m_hasImplicitArgIns ? ffHasImplicitArgsIn : 0)
            | (function->m_isAccessor ? ffIsAccessor : 0)
            | (function->m_isStaticNameFunction ? ffIsStaticNameFunction : 0)
            | (function->m_isNamedFunctionExpression ? ffIsNamedFunctionExpression : 0)
            | (function->m_isNameIdentifierRef ? ffIsNameIdentifierRef : 0)
            | (function->m_isGlobalFunc ? ffIsGlobalFunc : 0)
            | (function->m_dontInline ? ffDontInline : 0)
            | (function->m_isStrictMode ? ffIsStrictMode : 0)
            | (function->m_doBackendArgumentsOptimization ? ffDoBackendArgumentsOptimization : 0)
            | (function->m_doScopeObjectCreation ? ffDoScopeObjectCreation : 0)
            | (function->m_usesArgumentsObject ? ffUsesArgumentsObject : 0)
            | (function->m_isEval ? ffIsEval : 0)
            | (function->m_isDynamicFunction ? ffIsDynamicFunction : 0)
            | (isAnonymous ? ffIsAnonymous : 0)
            | (function->m_isMethod ? ffIsMethod : 0)
            | (function->m_isClassMember ? ffIsClassMember : 0)
#ifdef ASMJS_PLAT
            | (function->m_isAsmjsMode ? ffIsAsmJsMode : 0)
            | (function->m_isAsmJsFunction ? ffIsAsmJsFunction : 0)
#endif
            ;

        FunctionBody *functionBody = nullptr;
        if (function->IsFunctionBody())
        {
            functionBody = function->GetFunctionBody();

            bitFlags |=
                (functionBody->m_isFuncRegistered ? ffIsFuncRegistered : 0)
                | (functionBody->m_hasAllNonLocalReferenced ? ffhasAllNonLocalReferenced : 0)
                | (functionBody->m_hasSetIsObject ? ffhasSetIsObject : 0)
                | (functionBody->m_CallsEval ? ffhasSetCallsEval : 0)
                | (functionBody->m_ChildCallsEval ? ffChildCallsEval : 0)
                | (functionBody->m_hasReferenceableBuiltInArguments ? ffHasReferenceableBuiltInArguments : 0)
                | (functionBody->m_isParamAndBodyScopeMerged ? ffIsParamAndBodyScopeMerged : 0);
        }

        PrependConstantInt32(builder, _u("BitFlags"), bitFlags);

        if (!isAnonymous)
        {
            const char16* displayName = function->GetDisplayName();
            uint displayNameLength = function->m_displayNameLength;
            PrependString16(builder, _u("Display Name"), displayName, (displayNameLength + 1) * sizeof(char16));
        }

        PrependInt32(builder, _u("Relative Function ID"), function->GetLocalFunctionId() - topFunctionId); // Serialized function ids are relative to the top function ID
        auto attributes = function->GetAttributes();
        AssertMsg((attributes &
            ~(FunctionInfo::Attributes::ErrorOnNew
                | FunctionInfo::Attributes::SuperReference
                | FunctionInfo::Attributes::Lambda
                | FunctionInfo::Attributes::Async
                | FunctionInfo::Attributes::CapturesThis
                | FunctionInfo::Attributes::Generator
                | FunctionInfo::Attributes::ClassConstructor
                | FunctionInfo::Attributes::BaseConstructorKind
                | FunctionInfo::Attributes::ClassMethod
                | FunctionInfo::Attributes::Method
                | FunctionInfo::Attributes::EnclosedByGlobalFunc
                | FunctionInfo::Attributes::AllowDirectSuper
                | FunctionInfo::Attributes::DeferredParse
                | FunctionInfo::Attributes::CanDefer
                | FunctionInfo::Attributes::ComputedName
                | FunctionInfo::Attributes::HomeObj)) == 0,
            "Only the ErrorOnNew|SuperReference|Lambda|CapturesThis|Generator|ClassConstructor|BaseConstructorKind|Async|ClassMember|Method|EnclosedByGlobalFunc|AllowDirectSuper|ComputedName|DeferredParse|CanDefer|HomeObj attributes should be set on a serialized function");
        if (attributes != FunctionInfo::Attributes::None)
        {
            definedFields.has_attributes = true;
            PrependInt32(builder, _u("Attributes"), attributes);
        }

        PrependInt32(builder, _u("Offset Into Source"), sourceDiff);
        PrependInt32(builder, _u("Offset Into Source for toString"), function->PrintableStartOffset());
        if (function->GetNestedCount() > 0)
        {
            definedFields.has_m_nestedCount = true;
            PrependInt32(builder, _u("Nested count"), function->GetNestedCount());
        }

        DeferredFunctionStub* deferredStubs = function->GetDeferredStubs();
        if (deferredStubs != nullptr
            && (attributes & FunctionInfo::Attributes::DeferredParse) != 0
            && GenerateParserStateCache())
        {
            definedFields.has_deferredStubs = true;
            AddDeferredStubs(builder, deferredStubs, function->GetNestedCount(), cache, true);
        }

        PrintOffsets* printOffsets = function->GetPrintOffsets();
        if (printOffsets != nullptr)
        {
            definedFields.has_printOffsets = true;
            PrependInt32(builder, _u("Start print offset"), printOffsets->cbStartPrintOffset);
            PrependInt32(builder, _u("End print offset"), printOffsets->cbEndPrintOffset);
        }

        ScopeInfo* scopeInfo = function->GetScopeInfo();
        if (scopeInfo != nullptr
            && (attributes & (FunctionInfo::Attributes::DeferredParse | FunctionInfo::Attributes::CanDefer)) != 0)
        {
            definedFields.has_scopeInfo = true;
            AddScopeInfo(builder, scopeInfo);
        }

#define PrependArgSlot PrependInt16
#define PrependRegSlot PrependInt32
#define PrependCharCount PrependInt32
#define PrependULong PrependInt32
#define PrependUInt16 PrependInt16
#define PrependUInt32 PrependInt32

#define DEFINE_FUNCTION_PROXY_FIELDS 1
#define DEFINE_PARSEABLE_FUNCTION_INFO_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
        if (function->##name != 0) { \
            definedFields.has_##name = true; \
            Prepend##serializableType(builder, _u(#name), function->##name); \
        }

#include "SerializableFunctionFields.h"

        AddPropertyIdsForScopeSlotArray(builder, function);

        if (functionBody != nullptr)
        {
            AddFunctionBody(builder, functionBody, srcInfo, definedFields);
        }

        // Lastly, write each of the lexically enclosed functions
        if (function->GetNestedCount())
        {
            auto nestedBodyList = Anew(alloc, BufferBuilderList, _u("Nest Function Bodies"));

            for (uint32 i = 0; i < function->GetNestedCount(); ++i)
            {
                auto nestedFunction = function->GetNestedFunc(i);
                if (nestedFunction == nullptr || !nestedFunction->HasParseableInfo())
                {
                    PrependConstantInt32(builder, _u("Empty Nested Function"), 0);
                }
                else
                {
                    auto nestedFunctionBuilder = Anew(alloc, BufferBuilderList, _u("Nested Function"));
                    nestedBodyList->list = nestedBodyList->list->Prepend(nestedFunctionBuilder, alloc);
                    auto offsetToNested = Anew(alloc, BufferBuilderRelativeOffset, _u("Offset To Nested Function"), nestedFunctionBuilder);
                    builder.list = builder.list->Prepend(offsetToNested, alloc);
                    AddFunction(*nestedFunctionBuilder, nestedFunction->GetParseableFunctionInfo(), srcInfo, cache);
                }
            }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("End Function Body"), magicEndOfFunctionBody);
#endif

            builder.list = builder.list->Prepend(nestedBodyList, alloc);
        }
        else
        {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            PrependInt32(builder, _u("End Function Body"), magicEndOfFunctionBody);
#endif
        }

        // Reverse to put prepended items in correct order
        builder.list = builder.list->ReverseCurrentList();
        PrependStruct<SerializedFieldList>(builder, _u("Serialized Field List"), &definedFields);

        return S_OK;
    }

    HRESULT AddTopFunctionBody(FunctionBody * function, SRCINFO const * srcInfo, ByteCodeCache* cache)
    {
        topFunctionId = function->GetLocalFunctionId();
        functionCount.value = srcInfo->sourceContextInfo->nextLocalFunctionId;
        return AddFunction(functionsTable, function, srcInfo, cache);
    }

    HRESULT AddOneScopeInfo(BufferBuilderList & builder, ScopeInfo* scopeInfo, LocalScopeInfoId parentId = InvalidLocalScopeInfoId)
    {
        BufferBuilderInt32* startOfScopeInfo = nullptr;
        PrependInt32(builder, _u("ScopeInfo symbol count"), scopeInfo->symbolCount, &startOfScopeInfo);

        BufferBuilderRelativeOffset* offsetToScopeInfo = Anew(alloc, BufferBuilderRelativeOffset, _u("Offset To ScopeInfo"), startOfScopeInfo);
        this->scopeInfoRelativeOffsets.list = this->scopeInfoRelativeOffsets.list->Prepend(offsetToScopeInfo, alloc);

        FunctionInfo* functionInfo = scopeInfo->functionInfo;
        uint relativeFunctionId = 0;
        if (functionInfo != nullptr)
        {
            relativeFunctionId = functionInfo->GetLocalFunctionId() - topFunctionId;
        }
        PrependUInt32(builder, _u("ScopeInfo FunctionInfo relative id"), relativeFunctionId);

        ScopeInfoFlags scopeInfoFlags = (ScopeInfoFlags)
            ((scopeInfo->isDynamic ? sifIsDynamic : sifNone)
                | (scopeInfo->isObject ? sifIsObject : sifNone)
                | (scopeInfo->mustInstantiate ? sifMustInstantiate : sifNone)
                | (scopeInfo->isCached ? sifIsCached : sifNone)
                | (scopeInfo->hasLocalInClosure ? sifHasLocalInClosure : sifNone)
                | (scopeInfo->isGeneratorFunctionBody ? sifIsGeneratorFunctionBody : sifNone)
                | (scopeInfo->isAsyncFunctionBody ? sifIsAsyncFunctionBody : sifNone));

        PrependByte(builder, _u("ScopeInfo flags"), scopeInfoFlags);
        PrependInt32(builder, _u("ScopeInfo scope type"), scopeInfo->scopeType);
        PrependInt32(builder, _u("ScopeInfo scope id"), scopeInfo->scopeId);

        OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("Adding ScopeInfo (0x%p). Flags: %u. Type: %d. ScopeId: %d. Symbol count: %u\n"), scopeInfo, scopeInfoFlags, scopeInfo->scopeType, scopeInfo->scopeId, scopeInfo->symbolCount);

        for (int i = 0; i < scopeInfo->symbolCount; i++)
        {
            ScopeInfo::SymbolInfo* sym = scopeInfo->symbols + i;

            SymbolInfoFlags symbolInfoFlags = (SymbolInfoFlags)
                ((sym->hasFuncAssignment ? syifHasFuncAssignment : syifNone)
                    | (sym->isBlockVariable ? syifIsBlockVariable : syifNone)
                    | (sym->isConst ? syifIsConst : syifNone)
                    | (sym->isFuncExpr ? syifIsFuncExpr : syifNone)
                    | (sym->isModuleExportStorage ? syifIsModuleExportStorage : syifNone)
                    | (sym->isModuleImport ? syifIsModuleImport : syifNone));

            PrependByte(builder, _u("SymbolInfo flags"), symbolInfoFlags);
            PrependByte(builder, _u("SymbolInfo symbol type"), (BYTE)sym->symbolType);

            PropertyId symPropertyId = sym->propertyId;
            if (scopeInfo->areNamesCached)
            {
                Assert(sym->name != nullptr);
                symPropertyId = sym->name->GetPropertyId();
            }

            PropertyId propertyId = encodePossiblyBuiltInPropertyId(symPropertyId);
            PrependInt32(builder, _u("SymbolInfo property id"), propertyId);

            OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("\t\tSymbolInfo (0x%p). Flags: %u. Type: %d. PropertyId: %u (%u)\n"), sym, symbolInfoFlags, sym->symbolType, sym->propertyId, symPropertyId);
        }

        bool hasParent = scopeInfo->parent != nullptr;
        PrependBool(builder, _u("ScopeInfo has parent"), hasParent);

        if (hasParent)
        {
            // Prepend an int32 here as a placeholder in case we need to add more bytes into builder for the parent
            auto entry = Anew(alloc, BufferBuilderInt32, _u("ScopeInfo parent LocalId"), InvalidLocalScopeInfoId);
            builder.list = builder.list->Prepend(entry, alloc);

            // If we were not passed a valid pointer id, try and look it up in the map
            if (parentId == InvalidLocalScopeInfoId)
            {
                parentId = GetScopeInfoId(scopeInfo->parent);
            }

            // Update our placeholder with the real id of the parent
            entry->value = parentId;
        }

        return S_OK;
    }

    LocalScopeInfoId GetScopeInfoId(ScopeInfo* scopeInfo)
    {
        LocalScopeInfoId localScopeInfoId = InvalidLocalScopeInfoId;
        if (scopeInfoToScopeInfoIdMap->TryGetValue(scopeInfo, &localScopeInfoId))
        {
            Assert(localScopeInfoId != InvalidLocalScopeInfoId);
        }
        else
        {
            Assert(scopeInfoToScopeInfoIdMap->Count() == this->scopeInfoCount.value);

            if (scopeInfo->parent != nullptr)
            {
                GetScopeInfoId(scopeInfo->parent);
            }

            // If the ScopeInfo wasn't already in the table, add a mapping for it.
            localScopeInfoId = this->scopeInfoCount.value++;
            scopeInfoToScopeInfoIdMap->AddNew(scopeInfo, localScopeInfoId);

            OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("Mapping ScopeInfo 0x%p to local id %u\n"), scopeInfo, localScopeInfoId);

            AddOneScopeInfo(this->scopeInfoTable, scopeInfo);
        }

        return localScopeInfoId;
    }

    HRESULT AddScopeInfo(BufferBuilderList & builder, ScopeInfo* scopeInfo)
    {
        LocalScopeInfoId localScopeInfoId = GetScopeInfoId(scopeInfo);
        PrependInt32(builder, _u("ScopeInfo LocalId"), localScopeInfoId);

        return S_OK;
    }

    HRESULT AddDeferredStubs(BufferBuilderList & builder, DeferredFunctionStub* deferredStubs, uint stubsCount, ByteCodeCache* cache, bool recursive)
    {
        AssertOrFailFast(!(deferredStubs == nullptr && stubsCount > 0));

        if (deferredStubs == nullptr || stubsCount == 0)
        {
            return S_OK;
        }

        for (uint i = 0; i < stubsCount; i++)
        {
            DeferredFunctionStub* currentStub = deferredStubs + i;

            PrependUInt32(builder, _u("Character Min"), currentStub->ichMin);
            PrependUInt32(builder, _u("Function flags"), currentStub->fncFlags);
            PrependStruct(builder, _u("Restore Point"), &(currentStub->restorePoint));

            // Add all the captured name ids
            IdentPtrSet *capturedNames = currentStub->capturedNamePointers;

            if (capturedNames != nullptr && capturedNames->Count() != 0)
            {
                uint capturedNamesCount = capturedNames->Count();
                auto iter = capturedNames->GetIterator();

                PrependUInt32(builder, _u("Captured Name Count"), capturedNamesCount);

                if (cache != nullptr)
                {
                    currentStub->capturedNameCount = capturedNamesCount;
                    currentStub->capturedNameSerializedIds = RecyclerNewArray(this->scriptContext->GetRecycler(), int, capturedNamesCount);
                    currentStub->byteCodeCache = cache;
                }

                uint j = 0;
                while (iter.IsValid())
                {
                    // The captured names are IdentPtr allocated in Parser arena memory.
                    // We have to convert them to indices into our string table to effectively
                    // serialize the names.
                    const IdentPtr& pid = iter.CurrentValueReference();
                    int capturedNameSerializedId = this->GetIdOfString(pid->Psz(), (pid->Cch() + 1) * sizeof(WCHAR));

                    if (cache != nullptr)
                    {
                        Assert(j < capturedNamesCount);
                        currentStub->capturedNameSerializedIds[j] = capturedNameSerializedId;
                    }

                    PrependInt32(builder, _u("Captured Name"), capturedNameSerializedId);

                    iter.MoveNext();
                    j++;
                }
            }
            else
            {
                PrependUInt32(builder, _u("Captured Name Count"), 0);
            }

            PrependUInt32(builder, _u("Nested Count"), currentStub->nestedCount);
            if (recursive)
            {
                AddDeferredStubs(builder, currentStub->deferredStubs, currentStub->nestedCount, cache, recursive);
            }
        }

        return S_OK;
    }
};

class ByteCodeBufferReader
{
public:
    ScriptContext * scriptContext;
    byte * raw;
    int magic;
    int totalSize;
    byte fileVersionScheme;
    int V1;
    int V2;
    int V3;
    int V4;
    byte architecture;
    int expectedFunctionBodySize;
    int expectedBuildInPropertyCount;
    int expectedOpCodeCount;
    int firstFunctionId;
    int functionCount;
    uint scopeInfoCount;
    const byte * string16s;
    int string16Count;
    const unaligned StringIndexRecord * string16IndexTable;
    const byte * string16Table;
    int lineInfoCacheCount;
    const byte * lineInfoCaches;
    const charcount_t * lineCharacterOffsetCacheBuffer;
    const charcount_t * lineByteOffsetCacheBuffer;
    const byte * functions;
    const byte * scopeInfoTable;
    const byte * scopeInfoRelativeOffsets;
    int sourceSize;
    int sourceCharLength;
    Utf8SourceInfo *utf8SourceInfo;
    uint sourceIndex;
    bool const isLibraryCode;
public:
    ByteCodeBufferReader(ScriptContext * scriptContext, byte * raw, bool isLibraryCode, int builtInPropertyCount)
        : scriptContext(scriptContext),
        raw(raw),
        magic(0),
        totalSize(0),
        fileVersionScheme(0),
        V1(0),
        V2(0),
        V3(0),
        V4(0),
        architecture(0),
        expectedFunctionBodySize(sizeof(unaligned FunctionBody)),
        expectedBuildInPropertyCount(builtInPropertyCount),
        expectedOpCodeCount((int)OpCode::Count),
        firstFunctionId(0),
        functionCount(0),
        scopeInfoCount(0),
        string16s(nullptr),
        string16Count(0),
        string16IndexTable(nullptr),
        string16Table(nullptr),
        lineInfoCacheCount(0),
        lineInfoCaches(nullptr),
        lineCharacterOffsetCacheBuffer(nullptr),
        lineByteOffsetCacheBuffer(nullptr),
        functions(nullptr),
        scopeInfoTable(nullptr),
        scopeInfoRelativeOffsets(nullptr),
        sourceSize(0),
        sourceCharLength(0),
        utf8SourceInfo(nullptr),
        sourceIndex(0),
        isLibraryCode(isLibraryCode)
    {
        if (isLibraryCode)
        {
            expectedFunctionBodySize = 0;
            expectedOpCodeCount = 0;
        }
    }

    static const byte* ReadFunctionBodyFlags(const byte * buffer, size_t remainingBytes, FunctionBody::FunctionBodyFlags * value)
    {
        Assert(remainingBytes >= sizeof(FunctionBody::FunctionBodyFlags));
        *value = *(FunctionBody::FunctionBodyFlags*) buffer;
        return buffer + sizeof(FunctionBody::FunctionBodyFlags);
    }

    const byte* ReadFunctionBodyFlags(const byte * buffer, FunctionBody::FunctionBodyFlags * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadFunctionBodyFlags(buffer, remainingBytes, value);
    }

    const byte* ReadBool(const byte * buffer, _Out_ bool * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        Assert(remainingBytes >= sizeof(bool));
        *value = *buffer ? true : false;
        return buffer + sizeof(bool);
    }

    static const byte * ReadByte(const byte * buffer, size_t remainingBytes, byte * value)
    {
        Assert(remainingBytes>=sizeof(byte));
        *value = *(byte*)buffer;
        return buffer + sizeof(byte);
    }

    const byte * ReadByte(const byte * buffer, byte * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadByte(buffer, remainingBytes, value);
    }

    static const byte * ReadConstantSizedInt16(const byte * buffer, size_t remainingBytes, int16 * value)
    {
        Assert(remainingBytes >= sizeof(int16));
        *value = *(int16 *)buffer;
        return buffer + sizeof(int16);
    }

    const byte * ReadConstantSizedInt16(const byte * buffer, int16 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadConstantSizedInt16(buffer, remainingBytes, value);
    }

    static const byte * ReadInt16(const byte * buffer, size_t remainingBytes, int16 * value)
    {
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<int16>(buffer, remainingBytes, value);
#else
        Assert(remainingBytes>=sizeof(int16));
        *value = *(int16 *) buffer;
        return buffer + sizeof(int16);
#endif
    }

    const byte * ReadInt16(const byte * buffer, int16 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt16(buffer, remainingBytes, value);
    }

    static const byte * ReadConstantSizedInt64(const byte * buffer, size_t remainingBytes, int64 * value)
    {
        Assert(remainingBytes >= sizeof(int64));
        *value = *(int64 *)buffer;
        return buffer + sizeof(int64);
    }

    const byte * ReadConstantSizedInt64(const byte * buffer, int64 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadConstantSizedInt64(buffer, remainingBytes, value);
    }

    static const byte * ReadConstantSizedInt32(const byte * buffer, size_t remainingBytes, int * value)
    {
        Assert(remainingBytes >= sizeof(int));
        *value = *(int *) buffer;
        return buffer + sizeof(int);
    }

    const byte * ReadConstantSizedInt32(const byte * buffer, int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadConstantSizedInt32(buffer, remainingBytes, value);
    }

    const byte * ReadConstantSizedUInt32(const byte * buffer, uint * value)
    {
        return ReadConstantSizedInt32(buffer, (int *)value);
    }

    static const byte * ReadInt32(const byte * buffer, size_t remainingBytes, int * value)
    {
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<int>(buffer, remainingBytes, value);
#else
        Assert(remainingBytes >= sizeof(int));
        return ReadConstantSizedInt32(buffer, remainingBytes, value);
#endif
    }

    const byte * ReadInt32(const byte * buffer, int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, value);
    }

    const byte * ReadCharCount(const byte * buffer, size_t remainingBytes, charcount_t * value)
    {
#if VARIABLE_INT_ENCODING
        return ReadVariableInt<charcount_t>(buffer, remainingBytes, value);
#else
        Assert(remainingBytes >= sizeof(charcount_t));
        *value = *(charcount_t *) buffer;
        return buffer + sizeof(charcount_t);
#endif
    }

    const byte * ReadCharCount(const byte * buffer, charcount_t * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadCharCount(buffer, remainingBytes, value);
    }

    static const byte * ReadFloat(const byte * buffer, size_t remainingBytes, float * value)
    {
        Assert(remainingBytes >= sizeof(float));
        *value = *(float *)buffer;
        return buffer + sizeof(float);
    }

    const byte * ReadFloat(const byte * buffer, float * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadFloat(buffer, remainingBytes, value);
    }

    static const byte * ReadDouble(const byte * buffer, size_t remainingBytes, double * value)
    {
        Assert(remainingBytes>=sizeof(double));
        *value = *(double *)buffer;
        return buffer + sizeof(double);
    }

    const byte * ReadDouble(const byte * buffer, double * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadDouble(buffer, remainingBytes, value);
    }

    static const byte * ReadSIMDValue(const byte * buffer, size_t remainingBytes, SIMDValue * value)
    {
        Assert(remainingBytes >= sizeof(SIMDValue));
        *value = *(SIMDValue *)buffer;
        return buffer + sizeof(SIMDValue);
    }

    const byte * ReadSIMDValue(const byte * buffer, SIMDValue * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadSIMDValue(buffer, remainingBytes, value);
    }

    const byte * ReadUInt16(const byte * buffer, uint16 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt16(buffer, remainingBytes, (int16*)value);
    }

    const byte * ReadUInt32(const byte * buffer, unsigned int * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadULong(const byte * buffer, uint32 * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadRegSlot(const byte * buffer, RegSlot * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadArgSlot(const byte * buffer, ArgSlot * value)
    {
        auto remainingBytes = (raw + totalSize) - buffer;
        return ReadInt32(buffer, remainingBytes, (int*)value);
    }

    const byte * ReadConstantSizedInt32NoSize(const byte * buffer, int * value)
    {
        *value = *(int *)buffer;
        return buffer + sizeof(int);
    }

    template <typename TStructType>
    const byte * ReadStruct(const byte * buffer, serialization_alignment TStructType ** value)
    {
        *value = (serialization_alignment TStructType*)buffer;
        return buffer + sizeof(serialization_alignment TStructType);
    }

    const byte * ReadOffsetAsPointer(const byte * buffer, byte const ** value)
    {
        int offset;
        auto next = ReadConstantSizedInt32(buffer, &offset);
        if (offset == 0)
        {
            *value = nullptr;
            return next;
        }
        *value = raw + offset;
        return next;
    }
    template<typename Fn>
    const byte * ReadByteBlock(const byte * buffer, Fn fn)
    {
        int contentLength;
        buffer = ReadInt32(buffer, &contentLength);

        fn(contentLength, buffer);
        return buffer + contentLength;
    }

    const byte * ReadAuxiliary(const byte * buffer, FunctionBody * functionBody)
    {
        const byte * current = buffer;
        uint32 countOfAuxiliaryStructure;
        current = ReadUInt32(current, &countOfAuxiliaryStructure);
        Assert(countOfAuxiliaryStructure != 0);

        uint32 sizeOfAuxiliaryBlock;
        uint32 sizeOfAuxiliaryContextBlock;
        current = ReadUInt32(current, &sizeOfAuxiliaryBlock);
        current = ReadUInt32(current, &sizeOfAuxiliaryContextBlock);

        ByteBlock * auxBlock = sizeOfAuxiliaryBlock?
            ByteBlock::New(scriptContext->GetRecycler(), nullptr, sizeOfAuxiliaryBlock) : nullptr;
        ByteBlock * auxContextBlock = sizeOfAuxiliaryContextBlock?
            ByteBlock::New(scriptContext->GetRecycler(), nullptr, sizeOfAuxiliaryContextBlock) : nullptr;

        for (uint i = 0; i < countOfAuxiliaryStructure; i++)
        {
            typedef serialization_alignment const SerializedAuxiliary TBase;
            auto part = (serialization_alignment const SerializedAuxiliary * )current;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            Assert(part->auxMagic == magicStartOfAux);
#endif
            switch(part->kind)
            {
            default:
                AssertMsg(false, "Unexpected auxiliary kind");
                Throw::FatalInternalError();
                break;

            case sakVarArrayIntCount:
                current = DeserializeVarArray<VarArray>(scriptContext, current, auxBlock);
                break;

            case sakVarArrayVarCount:
                current = DeserializeVarArray<VarArrayVarCount>(scriptContext, current, auxContextBlock);
                break;

            case sakIntArray:
                current = DeserializeIntArray(scriptContext, current, auxBlock);
                break;

            case sakFloatArray:
                current = DeserializeFloatArray(scriptContext, current, auxBlock);
                break;

            case sakPropertyIdArray:
                current = DeserializePropertyIdArray(scriptContext, current, auxBlock, functionBody);
                break;

            case sakFuncInfoArray:
                current = DeserializeFuncInfoArray(scriptContext, current, auxBlock);
                break;
            }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
            int magicEnd;
            current = ReadInt32(current, &magicEnd);
            Assert(magicEnd == magicEndOfAux);
#endif
        }
        functionBody->SetAuxiliaryData(auxBlock);
        functionBody->SetAuxiliaryContextData(auxContextBlock);

        return current;
    }

    LPCWSTR GetString16ById(int id, bool* isPropertyRecord = nullptr)
    {
        if (id == 0xffffffff)
        {
            return nullptr;
        }
        if(!(id >= this->expectedBuildInPropertyCount && id <= string16Count + this->expectedBuildInPropertyCount))
        {
            Assert(false);
        }
        const unaligned StringIndexRecord* record = string16IndexTable + (id - this->expectedBuildInPropertyCount);
        if(isPropertyRecord)
        {
            *isPropertyRecord = record->isPropertyRecord;
        }
        auto offset = record->offset;
        auto addressOfString = raw + offset;
        return (LPCWSTR)addressOfString;
    }

    uint32 GetString16LengthById(int id)
    {
        if(!(id >= this->expectedBuildInPropertyCount && id<=string16Count + this->expectedBuildInPropertyCount))
        {
            Assert(false);
        }
        LPCWSTR s1 = GetString16ById(id);
        LPCWSTR s2 = GetString16ById(id + 1);
        auto result = s2 - s1 - 1;
        Assert(result <= UINT_MAX);
        return (uint32)result;
    }

    HRESULT ReadHeader()
    {
        auto current = ReadConstantSizedInt32NoSize(raw, &magic);
        if (magic != magicConstant)
        {
            AssertMsg(false, "Unrecognized magic constant in byte code file header. Is this really a bytecode file?");
            return E_FAIL;
        }
        current = ReadConstantSizedInt32NoSize(current, &totalSize);
        current = ReadByte(current, &fileVersionScheme);

        byte expectedFileVersionScheme = isLibraryCode? LibraryByteCodeVersioningScheme : CurrentFileVersionScheme;
#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema)
        {
            expectedFileVersionScheme = (byte)Js::Configuration::Global.flags.ForceSerializedBytecodeVersionSchema;
        }
#endif
        // Ignore the version scheme check if it is library code
        if (!isLibraryCode && fileVersionScheme != expectedFileVersionScheme)
        {
            // File version scheme is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }

        DWORD expectedV1 = 0;
        DWORD expectedV2 = 0;
        DWORD expectedV3 = 0;
        DWORD expectedV4 = 0;

        switch (expectedFileVersionScheme)
        {
        case EngineeringVersioningScheme:
            {
                Js::VerifyCatastrophic(!isLibraryCode);
                Js::VerifyOkCatastrophic(AutoSystemInfo::GetJscriptFileVersion(&expectedV1, &expectedV2, &expectedV3, &expectedV4));
                break;
            }

        case ReleaseVersioningScheme:
            {
                Js::VerifyCatastrophic(!isLibraryCode);
                auto guidDWORDs = (DWORD*)(&byteCodeCacheReleaseFileVersion);
                expectedV1 = guidDWORDs[0];
                expectedV2 = guidDWORDs[1];
                expectedV3 = guidDWORDs[2];
                expectedV4 = guidDWORDs[3];
                break;
            }

        case LibraryByteCodeVersioningScheme:
            {
                // To keep consistent library code between Chakra.dll and ChakraCore.dll, use a fixed version.
                // This goes hand in hand with the bytecode verification unit tests.
                Js::VerifyCatastrophic(isLibraryCode);
                expectedV1 = 0;
                expectedV2 = 0;
                expectedV3 = 0;
                expectedV4 = 0;
                break;
            }

        default:
            Throw::InternalError();
            break;
        }

#if ENABLE_DEBUG_CONFIG_OPTIONS
        if (Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion)
        {
            expectedV1 = Js::Configuration::Global.flags.ForceSerializedBytecodeMajorVersion;
            expectedV2 = 0;
            expectedV3 = 0;
            expectedV4 = 0;
        }
#endif
        current = ReadConstantSizedInt32(current, &V1);
        if ((DWORD)V1!=expectedV1)
        {
            // Incompatible major version
            return ByteCodeSerializer::InvalidByteCode;
        }
        // Library code is serialized with one build of the engine and then included into a subsequent build, so can't have match
        // on the build timestamp hash. Also want to share the generated bytecode between x86/ARM and debug/release, so skip the extra
        // checking. Will rework this validation entirely under TFS 555060
        current = ReadConstantSizedInt32(current, &V2);
        if ((DWORD)V2 != expectedV2)
        {
            // Incompatible minor version
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadConstantSizedInt32(current, &V3);
        if ((DWORD)V3 != expectedV3)
        {
            // Incompatible 3rd version part
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadConstantSizedInt32(current, &V4);
        if ((DWORD)V4 != expectedV4)
        {
            // Incompatible 4th version part
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadByte(current, &architecture);
        if (architecture!=magicArchitecture)
        {
            // This byte cache file was created with against a chakra running under a different architecture. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }

        int functionBodySize, buildInPropertyCount, opCodeCount;
        current = ReadInt32(current, &functionBodySize);
        if (functionBodySize != expectedFunctionBodySize)
        {
            // The size of function body didn't match. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &buildInPropertyCount);
        if (buildInPropertyCount!=expectedBuildInPropertyCount)
        {
            // This byte cache file was created with against a chakra that has a different number of built in properties. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &opCodeCount);
        if (opCodeCount != expectedOpCodeCount)
        {
            // This byte cache file was created with against a chakra that has a different number of built in properties. It is incompatible.
            return ByteCodeSerializer::InvalidByteCode;
        }
        current = ReadInt32(current, &sourceSize);
        current = ReadInt32(current, &sourceCharLength);

        current = ReadOffsetAsPointer(current, &string16s);
        current = ReadOffsetAsPointer(current, &lineInfoCaches);
        current = ReadOffsetAsPointer(current, &functions);
        current = ReadOffsetAsPointer(current, &scopeInfoRelativeOffsets);

        // Read strings header
        string16IndexTable = (StringIndexRecord*)ReadInt32(string16s, &string16Count);
        lineCharacterOffsetCacheBuffer = (charcount_t *)ReadInt32(lineInfoCaches, &lineInfoCacheCount);
        byte haslineByteOffsetCacheBuffer;
        current = ReadByte((byte*)lineCharacterOffsetCacheBuffer + sizeof(charcount_t) * lineInfoCacheCount, &haslineByteOffsetCacheBuffer);
        if (haslineByteOffsetCacheBuffer)
        {
            lineByteOffsetCacheBuffer = (charcount_t *)current;
        }
        else
        {
            lineByteOffsetCacheBuffer = nullptr;
        }

        string16Table = (byte*)(string16IndexTable + string16Count + 1);

        // string16Table is aligned to 2-bytes
        uint32 string16TableOffset = (uint32)(string16Table - raw);
        string16TableOffset = ::Math::Align(string16TableOffset, (uint32)sizeof(char16));
        string16Table = raw + string16TableOffset;

        // Consume ScopeInfo count and advance to the relative offsets
        scopeInfoRelativeOffsets = ReadUInt32(scopeInfoRelativeOffsets, &scopeInfoCount);
        scopeInfoTable = scopeInfoRelativeOffsets + sizeof(uint32) * scopeInfoCount;

        return S_OK;
    }

    const byte* ReadStringConstant(const byte* current, FunctionBody* function, _Out_ LPCWSTR * string, _Out_ uint32 * len)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartStringConstant);
#endif
        int stringId;
        current = ReadInt32(current, &stringId);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndStringConstant);
#endif
        *string = GetString16ById(stringId);
        *len = GetString16LengthById(stringId);

        return current;
    }

    const byte* ReadStringTemplateCallsiteConstant(const byte* current, FunctionBody* function, Var& var)
    {
        int arrayLength = 0;
        current = ReadInt32(current, &arrayLength);

        ScriptContext* scriptContext = function->GetScriptContext();

        ENTER_PINNED_SCOPE(Js::JavascriptArray, callsite);
        callsite = scriptContext->GetLibrary()->CreateArray(arrayLength);

        LPCWSTR string;
        uint32 len;
        uint32 rawlen = 0;

        for (int i = 0; i < arrayLength; i++)
        {
            current = ReadStringConstant(current, function, &string, &len);
            JavascriptString* str = JavascriptString::NewCopyBuffer(string, len, scriptContext);
            callsite->SetItemWithAttributes(i, str, PropertyEnumerable);
        }

        JavascriptArray* rawArray = scriptContext->GetLibrary()->CreateArray(arrayLength);

        for (int i = 0; i < arrayLength; i++)
        {
            current = ReadStringConstant(current, function, &string, &len);
            rawlen += len;

            JavascriptString* str = JavascriptString::NewCopyBuffer(string, len, scriptContext);
            rawArray->SetItemWithAttributes(i, str, PropertyEnumerable);
        }

        rawArray->Freeze();
        callsite->SetPropertyWithAttributes(Js::PropertyIds::raw, rawArray, PropertyNone, nullptr);
        callsite->Freeze();

        var = callsite;
        LEAVE_PINNED_SCOPE();

        return current;
    }

#ifdef ASMJS_PLAT
    const byte * ReadAsmJsConstantsTable(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfConstantTable);
#endif

        function->CreateConstantTable();

        auto constTable = function->GetConstTable();
        byte* tableEnd = (byte*)(constTable + function->GetConstantCount());

        for (int i = 0; i < WAsmJs::LIMIT; ++i)
        {
            WAsmJs::Types type = (WAsmJs::Types)i;
            WAsmJs::TypedSlotInfo* typedInfo = function->GetAsmJsFunctionInfo()->GetTypedSlotInfo(type);
            uint32 constCount = typedInfo->constCount;
            if (constCount > FunctionBody::FirstRegSlot)
            {
                uint32 typeSize = WAsmJs::GetTypeByteSize(type);
                byte* byteTable = ((byte*)constTable) + typedInfo->constSrcByteOffset;
                byteTable += typeSize * FunctionBody::FirstRegSlot;

                size_t remainingBytes = (raw + totalSize) - current;
                for (uint32 reg = FunctionBody::FirstRegSlot; reg < constCount; ++reg)
                {
                    switch (type)
                    {
                    case WAsmJs::INT32:   ReadConstantSizedInt32(current, remainingBytes, (int*)byteTable); break;
                    case WAsmJs::FLOAT32: ReadFloat(current, remainingBytes, (float*)byteTable); break;
                    case WAsmJs::FLOAT64: ReadDouble(current, remainingBytes, (double*)byteTable); break;
                    default:
                        Assert(UNREACHED);
                        Js::Throw::FatalInternalError();
                        break;
                    }
                    current += typeSize;
                    byteTable += typeSize;
                    remainingBytes -= typeSize;
                }

                if (byteTable > tableEnd)
                {
                    Assert(UNREACHED);
                    Js::Throw::FatalInternalError();
                }
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfConstantTable);
#endif

        return current;
    }
#endif

    const byte * ReadConstantsTable(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfConstantTable);
#endif

        function->CreateConstantTable();

        for (auto reg = FunctionBody::FirstRegSlot + 1; reg < function->GetConstantCount(); reg++) // Ignore first slot, it is always global or module root and has been preinitialized.
        {
            byte ct;
            current = ReadByte(current, &ct);
            switch(ct)
            {
            case ctString16:
                {
                    LPCWSTR string;
                    uint32 len;
                    current = ReadStringConstant(current, function, &string, &len);

                    function->RecordStrConstant(reg, string, len, false);
                    break;
                }
            case ctPropertyString16:
                {
                    LPCWSTR string;
                    uint32 len;
                    current = ReadStringConstant(current, function, &string, &len);

                    function->RecordStrConstant(reg, string, len, true);
                    break;
                }
            case ctStringTemplateCallsite:
                {
                    Var callsite = nullptr;
                    current = ReadStringTemplateCallsiteConstant(current, function, callsite);

                    function->RecordConstant(reg, callsite);
                    break;
                }
            case ctInt32:
                {
                    int value;
                    current = ReadConstantSizedInt32(current, &value);
                    function->RecordIntConstant(reg, value);
                    break;
                }
            case ctInt16:
                {
                    int16 value;
                    current = ReadConstantSizedInt16(current, &value);
                    function->RecordIntConstant(reg, value);
                    break;
                }
            case ctInt8:
                {
                    int8 value;
                    current = ReadByte(current, (byte *)&value);
                    function->RecordIntConstant(reg, value);
                    break;
                }
            case ctNull:
                function->RecordNullObject(reg);
                break;
            case ctUndefined:
                function->RecordUndefinedObject(reg);
                break;
            case ctNumber:
                {
                    double value;
                    current = ReadDouble(current, &value);
                    function->RecordFloatConstant(reg, value);
                    break;
                }
            case ctNullDisplay:
                function->RecordNullDisplayConstant(reg);
                break;
            case ctStrictNullDisplay:
                function->RecordStrictNullDisplayConstant(reg);
                break;
            case ctTrue:
                function->RecordTrueObject(reg);
                break;
            case ctFalse:
                function->RecordFalseObject(reg);
                break;
            default:
                AssertMsg(UNREACHED, "Unexpected object type in ReadConstantsTable");
                break;
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfConstantTable);
#endif

        return current;
    }

    const byte * ReadPropertyIdsForScopeSlotArray(const byte * current, ByteCodeCache* cache, ParseableFunctionInfo * function)
    {
        if (function->scopeSlotArraySize == 0)
        {
            return current;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfPropertyIdsForScopeSlotArray);
#endif

        function->SetPropertyIdsForScopeSlotArray(RecyclerNewArrayLeaf(scriptContext->GetRecycler(), Js::PropertyId, function->scopeSlotArraySize), function->scopeSlotArraySize, function->paramScopeSlotArraySize);

        for (uint i = 0; i < function->scopeSlotArraySize; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = cache->LookupPropertyId(value);
            function->GetPropertyIdsForScopeSlotArray()[i] = propertyId;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfPropertyIdsForScopeSlotArray);
#endif

        return current;
    }

    const byte * ReadSlotIdInCachedScopeToNestedIndexArray(const byte * current, FunctionBody * functionBody)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfSlotIdToNestedIndexArray);
#endif
        uint32 count;
        current = ReadUInt32(current, &count);

        Js::AuxArray<uint32> * slotIdInCachedScopeToNestedIndexArray = functionBody->AllocateSlotIdInCachedScopeToNestedIndexArray(count);

        uint32 value;
        for (uint i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &value);
            slotIdInCachedScopeToNestedIndexArray->elements[i] = value;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfSlotIdToNestedIndexArray);
#endif

        return current;
    }
    
#if ENABLE_NATIVE_CODEGEN
    const byte * ReadCallSiteToCallApplyCallSiteArray(const byte * current, FunctionBody * functionBody)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfCallSiteToCallApplyCallSiteArray);
#endif
        Js::ProfileId * callSiteToCallApplyCallSiteArray = functionBody->CreateCallSiteToCallApplyCallSiteArray();

        Js::ProfileId value;
        for (Js::ProfileId i = 0; i < functionBody->GetProfiledCallSiteCount(); i++)
        {
            current = ReadUInt16(current, &value);
            callSiteToCallApplyCallSiteArray[i] = value;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfCallSiteToCallApplyCallSiteArray);
#endif

        return current;
    }
#endif

    const byte * ReadSlotArrayDebuggerScopeProperties(const byte * current, FunctionBody* function, DebuggerScope* debuggerScope, uint propertyCount)
    {
        Assert(function);
        Assert(debuggerScope);
        if (propertyCount == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        for (uint i = 0u; i < propertyCount; ++i)
        {
            // Read the slot array index and property ID for each property (for heap enum to use).  The remaining properties
            // are needed for the debugger and will be filled in when generating byte code.
            int value;
            current = ReadInt32(current, &value);
            RegSlot slotIndex = value;

            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(value);
            debuggerScope->AddProperty(slotIndex, propertyId, DebuggerScopePropertyFlags_None);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfDebuggerScopeProperties);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return current;
    }

    const byte * ReadSlotArrayDebuggerScopes(const byte * current, FunctionBody * function, uint debuggerScopeCount)
    {
        Assert(function);
        Assert(debuggerScopeCount != 0);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        AssertMsg(function->GetScopeObjectChain() == nullptr, "Scope chain should not exist before deserialization.");
        function->SetScopeObjectChain(RecyclerNew(scriptContext->GetRecycler(), ScopeObjectChain, scriptContext->GetRecycler()));

        int currentScopeOffset = 0;
        for (uint i = 0u; i < debuggerScopeCount; i++)
        {
            int scopeIndex;
            current = ReadInt32(current, &scopeIndex);
            DebuggerScope* slotArrayDebuggerScope = nullptr;
            AssertMsg(currentScopeOffset <= scopeIndex, "Scope indices were not inserted into the serialized byte code in ascending order.");
            while (currentScopeOffset <= scopeIndex)
            {
                // Fill the chain with dummy scopes until we reach the slot array scope we're on.
                // These non-slot array scopes are only needed for the debugger and will be filled in
                // properly during byte code generation (when attaching).
                // We also don't need to worry about the parenting/sibling chain, as this will be built when
                // generating bytecode as well.
                slotArrayDebuggerScope = function->AddScopeObject(Js::DiagUnknownScope, 0, Constants::NoRegister);
                ++currentScopeOffset;
            }

            Assert(slotArrayDebuggerScope);

            // Create the slot array properties.
            int propertyCount;
            current = ReadInt32(current, &propertyCount);
            current = ReadSlotArrayDebuggerScopeProperties(current, function, slotArrayDebuggerScope, propertyCount);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfDebuggerScopes);
#endif // BYTE_CODE_MAGIC_CONSTANTS

        return current;
    }

    const byte * ReadCacheIdToPropertyIdMap(const byte * current, FunctionBody * function)
    {
        uint count = function->GetInlineCacheCount();
        if (count == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfCacheIdToPropIdMap);
#endif

        function->CreateCacheIdToPropertyIdMap();

        for (uint i = 0; i < count; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(value);
            function->SetPropertyIdForCacheId(i, propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfCacheIdToPropIdMap);
#endif
#if DBG
        function->VerifyCacheIdToPropertyIdMap();
#endif
        return current;
    }

    const byte * ReadReferencedPropertyIdMap(const byte * current, FunctionBody * function)
    {
        uint count = function->GetReferencedPropertyIdCount();
        if (count == 0)
        {
            return current;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfReferencedPropIdMap);
#endif

        function->CreateReferencedPropertyIdMap();

        for (uint i = 0; i < count; i++)
        {
            int value;
            current = ReadInt32(current, &value);
            PropertyId propertyId = function->GetByteCodeCache()->LookupNonBuiltinPropertyId(value);
            function->SetReferencedPropertyIdWithMapIndex(i, propertyId);
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfReferencedPropIdMap);
#endif
#if DBG
        function->VerifyReferencedPropertyIdMap();
#endif
        return current;
    }

    // Read a growing Uint32 array
    const byte * ReadGrowingUint32Array(const byte * current, JsUtil::GrowingUint32HeapArray ** arr)
    {
        int count = 0;
        current = ReadInt32(current, &count);
        if (count == 0)
        {
           (*arr) = nullptr;
           return current;
        }
        (*arr) = JsUtil::GrowingUint32HeapArray::Create(/*length=*/count);
        js_memcpy_s((*arr)->GetBuffer(), count * sizeof(uint32), current, count*sizeof(uint32));
        (*arr)->SetCount(count);
        current += count * sizeof(uint32);
        return current;
    }

    // Read a small span sequence
    const byte * ReadSmallSpanSequence(const byte * current, SmallSpanSequence ** smallSpanSequence)
    {
        (*smallSpanSequence) = HeapNew(SmallSpanSequence);
        current = ReadInt32(current, &(*smallSpanSequence)->baseValue);
        current = ReadGrowingUint32Array(current, &(*smallSpanSequence)->pStatementBuffer); // CONSIDER: It would be really nice to change GrowingUint32Array to something with a fixed, readonly layout
        current = ReadGrowingUint32Array(current, &(*smallSpanSequence)->pActualOffsetList);
        return current;
    }

    void ReadSourceInfo(const byte * functionBytes, int& lineNumber, int& columnNumber, bool& m_isEval, bool& m_isDynamicFunction)
    {
        int displayNameId;
        unsigned int bitflags;
        this->ReadFunctionBodyHeader(functionBytes, displayNameId, lineNumber, columnNumber, bitflags);
        m_isEval = (bitflags & ffIsEval) ? true : false;
        m_isDynamicFunction = (bitflags & ffIsDynamicFunction) ? true : false;
    }

    const byte * ReadFunctionBodyHeader(const byte * functionBytes, int& displayNameId, int& lineNumber, int& columnNumber, unsigned int& bitflags)
    {
        serialization_alignment SerializedFieldList* definedFields = (serialization_alignment SerializedFieldList*) functionBytes;

        // Basic function body constructor arguments
        const byte * current = functionBytes + sizeof(serialization_alignment SerializedFieldList);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfFunctionBody);
#endif
        if (definedFields->has_m_lineNumber)
        {
            current = ReadInt32(current, &lineNumber);
        }
        else
        {
            lineNumber = 0;
        }

        if (definedFields->has_m_columnNumber)
        {
            current = ReadInt32(current, &columnNumber);
        }
        else
        {
            columnNumber = 0;
        }

        current = ReadConstantSizedUInt32(current, &bitflags);

        if (bitflags & ffIsAnonymous)
        {
            displayNameId = -1;
        }
        else
        {
            current = ReadInt32(current, &displayNameId);
        }
        return current;
    }

    const byte * ReadPropertyIdOfFormals(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant = 0;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfPropIdsOfFormals);
#endif

        uint32 count = 0;
        current = ReadUInt32(current, &count);

        byte extraSlotCount = 0;
        current = ReadByte(current, &extraSlotCount);

        PropertyIdArray * propIds = function->AllocatePropertyIdArrayForFormals((extraSlotCount + count) * sizeof(PropertyId), count, extraSlotCount);
        propIds->count = count;

        bool hadDuplicates = false;
        current = ReadBool(current, &hadDuplicates);
        propIds->hadDuplicates = hadDuplicates;

        bool has__proto__ = false;
        current = ReadBool(current, &has__proto__);
        propIds->has__proto__ = has__proto__;

        bool hasNonSimpleParams = false;
        current = ReadBool(current, &hasNonSimpleParams);
        propIds->hasNonSimpleParams = hasNonSimpleParams;

        int id = 0;
        for (uint i = 0; i < propIds->count; ++i)
        {
            current = ReadInt32(current, &id);
            PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(id);
            propIds->elements[i] = propertyId;
        }

        for (int i = 0; i < extraSlotCount; ++i)
        {
            current = ReadInt32(current, &id);
            propIds->elements[propIds->count + i] = id;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfPropIdsOfFormals);
#endif

        return current;
    }

#ifdef ASMJS_PLAT
    const byte * ReadAsmJsFunctionInfo(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfAsmJsFuncInfo);
#endif

        AsmJsFunctionInfo* funcInfo = function->AllocateAsmJsFunctionInfo();

        int retVal;
        current = ReadInt32(current, &retVal);
        funcInfo->SetReturnType(AsmJsRetType((AsmJsRetType::Which)retVal));

        ArgSlot argCount;
        current = ReadUInt16(current, &argCount);
        funcInfo->SetArgCount(argCount);

        ArgSlot argByteSize;
        current = ReadUInt16(current, &argByteSize);
        funcInfo->SetArgByteSize(argByteSize);

        ArgSlot argSizeArrayLength;
        current = ReadUInt16(current, &argSizeArrayLength);
        funcInfo->SetArgSizeArrayLength(argSizeArrayLength);
        uint* argArray = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), uint, argSizeArrayLength);
        funcInfo->SetArgsSizesArray(argArray);
        for (int i = 0; i < argSizeArrayLength; i++)
        {
            int32 size;
            current = ReadConstantSizedInt32(current, &size);
            argArray[i] = (uint32)size;
        }

        if (argCount > 0)
        {
            AsmJsVarType::Which * typeArray = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), AsmJsVarType::Which, argCount);
            funcInfo->SetArgTypeArray(typeArray);
            for (uint i = 0; i < argCount; i++)
            {
                current = ReadByte(current, (byte*)&typeArray[i]);
            }
        }

        bool boolVal;
        current = ReadBool(current, &boolVal);
        funcInfo->SetUsesHeapBuffer(boolVal);

        for (int i = WAsmJs::LIMIT - 1; i >= 0; --i)
        {
            serialization_alignment WAsmJs::TypedSlotInfo* info;
            current = ReadStruct<WAsmJs::TypedSlotInfo>(current, &info);
            WAsmJs::TypedSlotInfo* typedInfo = funcInfo->GetTypedSlotInfo((WAsmJs::Types)i);
            *typedInfo = *info;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfAsmJsFuncInfo);
#endif

        return current;
    }

    const byte * ReadAsmJsModuleInfo(const byte * current, FunctionBody * function)
    {
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int constant;
        current = ReadInt32(current, &constant);
        Assert(constant == magicStartOfAsmJsModuleInfo);
#endif
        AsmJsModuleInfo* moduleInfo = function->AllocateAsmJsModuleInfo();

        int count;
        current = ReadInt32(current, &count);
        moduleInfo->SetArgInCount(count);

        int exportsCount;
        current = ReadInt32(current, &exportsCount);
        moduleInfo->SetExportsCount(exportsCount);

        current = ReadInt32(current, &count);
        moduleInfo->InitializeSlotMap(count);

        int id;
        if (exportsCount > 0)
        {
            PropertyIdArray * propArray = moduleInfo->GetExportsIdArray();

            byte extraSlots;
            current = ReadByte(current, &extraSlots);
            propArray->extraSlots = extraSlots;

            bool boolVal;
            current = ReadBool(current, &boolVal);
            propArray->hadDuplicates = boolVal;

            current = ReadBool(current, &boolVal);
            propArray->has__proto__ = boolVal;

            current = ReadInt32(current, &count);
            propArray->count = count;

            for (uint i = 0; i < propArray->count; i++)
            {
                current = ReadInt32(current, &id);
                PropertyId propertyId = function->GetByteCodeCache()->LookupPropertyId(id);
                propArray->elements[i] = propertyId;
            }

            RegSlot* exportLocations = moduleInfo->GetExportsFunctionLocation();
            for (int i = 0; i < exportsCount; i++)
            {
                int32 loc;
                current = ReadConstantSizedInt32(current, &loc);
                exportLocations[i] = (uint32)loc;
            }
        }
        RegSlot regSlot;
        current = ReadUInt32(current, &regSlot);
        moduleInfo->SetExportFunctionIndex(regSlot);

        current = ReadInt32(current, &count);
        moduleInfo->SetVarCount(count);

        for (int i = 0; i < count; i++)
        {
            serialization_alignment AsmJsModuleInfo::ModuleVar * modVar;
            current = ReadStruct<AsmJsModuleInfo::ModuleVar>(current, &modVar);
            moduleInfo->SetVar(i, *modVar);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetVarImportCount(count);
        AsmJsModuleInfo::ModuleVarImport varImport;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &varImport.location);
            current = ReadByte(current, (byte*)&varImport.type);
            current = ReadInt32(current, &id);
            varImport.field = function->GetByteCodeCache()->LookupPropertyId(id);
            moduleInfo->SetVarImport(i, varImport);
        }


        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionImportCount(count);
        AsmJsModuleInfo::ModuleFunctionImport funcImport;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &funcImport.location);
            current = ReadInt32(current, &id);
            funcImport.field = function->GetByteCodeCache()->LookupPropertyId(id);
            moduleInfo->SetFunctionImport(i, funcImport);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionCount(count);

        AsmJsModuleInfo::ModuleFunction modFunc;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &modFunc.location);
            moduleInfo->SetFunction(i, modFunc);
        }

        current = ReadInt32(current, &count);
        moduleInfo->SetFunctionTableCount(count);

        AsmJsModuleInfo::ModuleFunctionTable funcTable;
        for (int i = 0; i < count; i++)
        {
            current = ReadUInt32(current, &funcTable.size);
            if (funcTable.size > 0)
            {
                funcTable.moduleFunctionIndex = RecyclerNewArray(this->scriptContext->GetRecycler(), RegSlot, funcTable.size);
            }
            else
            {
                funcTable.moduleFunctionIndex = nullptr;
            }
            for (uint j = 0; j < funcTable.size; j++)
            {
                current = ReadConstantSizedInt32(current, (int32*)&funcTable.moduleFunctionIndex[j]);
            }
            moduleInfo->SetFunctionTable(i, funcTable);
        }

        serialization_alignment AsmJsModuleMemory * modMem = (serialization_alignment AsmJsModuleMemory*)current;
        moduleInfo->SetModuleMemory(*modMem);
        current = current + sizeof(serialization_alignment AsmJsModuleMemory);

        current = ReadInt32(current, &count);
        for (int i = 0; i < count; i++)
        {

            current = ReadInt32(current, &id);
            PropertyId key = function->GetByteCodeCache()->LookupPropertyId(id);

            serialization_alignment AsmJsSlot * slot = (serialization_alignment AsmJsSlot*)current;
            current = current + sizeof(serialization_alignment AsmJsSlot);

            // copy the slot to recycler memory
            AsmJsSlot * recyclerSlot = RecyclerNew(scriptContext->GetRecycler(), AsmJsSlot);
            *recyclerSlot = *slot;
            moduleInfo->GetAsmJsSlotMap()->Add(key, recyclerSlot);
        }

        serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE> * mathBV = (serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE>*)current;
        current = current + sizeof(serialization_alignment BVStatic<ASMMATH_BUILTIN_SIZE>);
        moduleInfo->SetAsmMathBuiltinUsed(*mathBV);

        serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE> * arrayBV = (serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE>*)current;
        current = current + sizeof(serialization_alignment BVStatic<ASMARRAY_BUILTIN_SIZE>);
        moduleInfo->SetAsmArrayBuiltinUsed(*arrayBV);

        uint maxAccess;
        current = ReadUInt32(current, &maxAccess);
        moduleInfo->SetMaxHeapAccess(maxAccess);

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        current = ReadInt32(current, &constant);
        Assert(constant == magicEndOfAsmJsModuleInfo);
#endif

        return current;
    }
#endif

    // Read a function body
    HRESULT ReadFunctionBody(const byte * functionBytes, FunctionProxy ** functionProxy, Utf8SourceInfo* sourceInfo, ByteCodeCache * cache, NativeModule *nativeModule, bool deserializeThis, bool deserializeNested = true, Js::DeferDeserializeFunctionInfo* deferDeserializeFunctionInfo = NULL)
    {
        Assert(sourceInfo->GetSrcInfo()->moduleID == kmodGlobal);

        int displayNameId;
        int lineNumber;
        int columnNumber;
        unsigned int bitflags;
        const byte * current = this->ReadFunctionBodyHeader(functionBytes, displayNameId, lineNumber, columnNumber, bitflags);

        serialization_alignment SerializedFieldList* definedFields = (serialization_alignment SerializedFieldList*) functionBytes;

        FunctionProxy::SetDisplayNameFlags displayNameFlags = FunctionProxy::SetDisplayNameFlags::SetDisplayNameFlagsDontCopy;
        const char16* displayName = nullptr;
        if (bitflags & ffIsAnonymous)
        {
            displayName = Constants::AnonymousFunction;
        }
        else
        {
            if (deferDeserializeFunctionInfo != nullptr)
            {
                displayName = deferDeserializeFunctionInfo->GetDisplayName();
                if (deferDeserializeFunctionInfo->GetDisplayNameIsRecyclerAllocated())
                {
                    displayNameFlags = (FunctionProxy::SetDisplayNameFlags)(displayNameFlags | FunctionProxy::SetDisplayNameFlags::SetDisplayNameFlagsRecyclerAllocated);
                }
            }
            else
            {
                displayName = GetString16ById(displayNameId);
            }
        }

        uint displayNameLength = (bitflags & ffIsAnonymous) ? Constants::AnonymousFunctionLength :
            deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetDisplayNameLength() :
            GetString16LengthById(displayNameId);
        uint displayShortNameOffset = deferDeserializeFunctionInfo ? deferDeserializeFunctionInfo->GetShortDisplayNameOffset() : 0;
        int functionId;
        current = ReadInt32(current, &functionId);

        int32 attributes = 0;
        if (definedFields->has_attributes)
        {
            current = ReadInt32(current, &attributes);
        }

        uint32 offsetIntoSource = 0;
        uint32 offsetIntoSourcePrintable = 0;
        current = ReadUInt32(current, &offsetIntoSource);
        current = ReadUInt32(current, &offsetIntoSourcePrintable);

        int nestedCount = 0;
        if (definedFields->has_m_nestedCount)
        {
            current = ReadInt32(current, &nestedCount);
        }

        bool isDeferredFunction = (attributes & FunctionInfo::Attributes::DeferredParse) != 0;
        Field(DeferredFunctionStub*) deferredStubs = nullptr;
        if (definedFields->has_deferredStubs)
        {
            Assert(isDeferredFunction);
            current = ReadDeferredStubs(current, cache, nestedCount, &deferredStubs, true);
        }

        PrintOffsets* printOffsets = nullptr;
        if (definedFields->has_printOffsets)
        {
            printOffsets = RecyclerNewLeaf(this->scriptContext->GetRecycler(), PrintOffsets);
            current = ReadUInt32(current, &printOffsets->cbStartPrintOffset);
            current = ReadUInt32(current, &printOffsets->cbEndPrintOffset);
        }

        ScopeInfo* scopeInfo = nullptr;
        if (definedFields->has_scopeInfo)
        {
            Assert(attributes & (FunctionInfo::Attributes::DeferredParse | FunctionInfo::Attributes::CanDefer));
            current = ReadScopeInfo(current, cache, &scopeInfo);
        }

        if (!deserializeThis && !isDeferredFunction)
        {
            Assert(sourceInfo->GetSrcInfo()->moduleID == kmodGlobal);
            Assert(!deserializeNested);
            *functionProxy = DeferDeserializeFunctionInfo::New(this->scriptContext, nestedCount, functionId, cache, functionBytes, sourceInfo, displayName, displayNameLength, displayShortNameOffset, nativeModule, (FunctionInfo::Attributes)attributes);

            if (deferDeserializeFunctionInfo == nullptr && !this->isLibraryCode)
            {
                cache->RegisterFunctionIdToFunctionInfo(this->scriptContext, functionId, (*functionProxy)->GetFunctionInfo());
            }

            return S_OK;
        }

        ParseableFunctionInfo **function = (ParseableFunctionInfo **) functionProxy;
        uint functionNumber;

        if (deferDeserializeFunctionInfo)
        {
            functionNumber = deferDeserializeFunctionInfo->GetFunctionNumber();
        }
        else
        {
            functionNumber = scriptContext->GetThreadContext()->NewFunctionNumber();
        }

        if (definedFields->has_ConstantCount)
        {
            FunctionBody **functionBody = (FunctionBody **) function;

            *functionBody = FunctionBody::NewFromRecycler(this->scriptContext, nullptr /*displayName*/, 0 /*displayNameLength*/, 0 /*displayShortNameOffset*/, nestedCount,
                sourceInfo,
                functionNumber,
                sourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId,
                firstFunctionId + functionId, (FunctionInfo::Attributes)attributes,
                Js::FunctionBody::FunctionBodyFlags::Flags_None  // bytecode serializer will initialize
#ifdef PERF_COUNTERS
                , (deferDeserializeFunctionInfo != nullptr)
#endif
                );

            (*functionBody)->SetDisplayName(displayName, displayNameLength, displayShortNameOffset, displayNameFlags);

            Assert(!(*functionBody)->GetIsSerialized());
            (*functionBody)->SetByteCodeCache(cache);
            (*functionBody)->SetUtf8SourceInfo(utf8SourceInfo); // Set source info
            (*function)->m_utf8SourceHasBeenSet = true;
        }
        else
        {
            *function = ParseableFunctionInfo::New(this->scriptContext, nestedCount, firstFunctionId + functionId, utf8SourceInfo, displayName, displayNameLength, displayShortNameOffset, (FunctionInfo::Attributes)attributes,
                Js::FunctionBody::FunctionBodyFlags::Flags_None);

            if (deferredStubs != nullptr)
            {
                (*function)->SetDeferredStubs(deferredStubs);
            }
            if (printOffsets != nullptr)
            {
                (*function)->SetPrintOffsets(printOffsets);
            }
            if (scopeInfo != nullptr)
            {
                (*function)->SetScopeInfo(scopeInfo);
            }
        }

        if (deferDeserializeFunctionInfo == nullptr && !this->isLibraryCode)
        {
            cache->RegisterFunctionIdToFunctionInfo(this->scriptContext, functionId, (*function)->GetFunctionInfo());
        }

        // These fields are manually deserialized previously
        (*function)->m_lineNumber = lineNumber;
        (*function)->m_columnNumber = columnNumber;
        (*function)->m_isDeclaration = (bitflags & ffIsDeclaration) ? true : false;
        (*function)->m_hasImplicitArgIns = (bitflags & ffHasImplicitArgsIn) ? true : false;
        (*function)->m_isAccessor = (bitflags & ffIsAccessor) ? true : false;
        (*function)->m_isStaticNameFunction = (bitflags & ffIsStaticNameFunction) ? true : false;
        (*function)->m_isNamedFunctionExpression = (bitflags & ffIsNamedFunctionExpression) ? true : false;
        (*function)->m_isNameIdentifierRef  = (bitflags & ffIsNameIdentifierRef ) ? true : false;
        (*function)->m_isGlobalFunc = (bitflags & ffIsGlobalFunc) ? true : false;
        (*function)->m_dontInline = (bitflags & ffDontInline) ? true : false;
        (*function)->m_isStrictMode = (bitflags & ffIsStrictMode) ? true : false;
        (*function)->m_doBackendArgumentsOptimization = (bitflags & ffDoBackendArgumentsOptimization) ? true : false;
        (*function)->m_doScopeObjectCreation = (bitflags & ffDoScopeObjectCreation) ? true : false;
        (*function)->m_usesArgumentsObject = (bitflags & ffUsesArgumentsObject) ? true : false;
        (*function)->m_isEval = (bitflags & ffIsEval) ? true : false;
        (*function)->m_isDynamicFunction = (bitflags & ffIsDynamicFunction) ? true : false;
        (*function)->m_isMethod = (bitflags & ffIsMethod) ? true : false;
        (*function)->m_isClassMember = (bitflags & ffIsClassMember) ? true : false;

        // This is offsetIntoSource is the start offset in bytes as well.
        (*function)->m_cbStartOffset = offsetIntoSource;
        (*function)->m_sourceIndex = this->sourceIndex;

#define DEFINE_FUNCTION_PROXY_FIELDS 1
#define DEFINE_PARSEABLE_FUNCTION_INFO_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
        if (definedFields->has_##name == true) { \
            current = Read##serializableType(current, &(*function)->##name); \
        }
#include "SerializableFunctionFields.h"

        current = ReadPropertyIdsForScopeSlotArray(current, cache, *function);

        if (definedFields->has_ConstantCount)
        {
            FunctionBody **functionBody = (FunctionBody **)function;

#define DEFINE_FUNCTION_BODY_FIELDS 1
#define DECLARE_SERIALIZABLE_FIELD(type, name, serializableType) \
            if (definedFields->has_##name == true) { \
                current = Read##serializableType(current, &(*functionBody)->##name); \
            }

#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD_NO_CHECK(type, name, serializableType) \
            type tmp##name=0; \
            current = Read##serializableType(current, &tmp##name); \
            (*functionBody)->Set##name##(tmp##name);

#define DECLARE_SERIALIZABLE_ACCESSOR_FIELD(type, name, serializableType, defaultValue) \
            if (definedFields->has_##name == true) { \
                DECLARE_SERIALIZABLE_ACCESSOR_FIELD_NO_CHECK(type, name, serializableType); \
            }

#include "SerializableFunctionFields.h"

            // TODO-STACK-NESTED-FUNC: Defer deserialize function doesn't have parent pointer, can't do stack nested func yet
            // The flags field is set to by default to Flags_HasNoExplicitReturnValue which means if it's serialized, the field will be set
            // in the definedFields struct. If it's not set, that means that the flag was explicitly set to Flags_None so we'll have to set
            // that here.
            if (definedFields->has_flags == false)
            {
                (*function)->flags = FunctionBody::FunctionBodyFlags::Flags_None;
            }
            else
            {
                (*function)->flags = (FunctionBody::FunctionBodyFlags)((*function)->flags & ~FunctionBody::Flags_StackNestedFunc);
            }

            if (definedFields->has_m_envDepth == false)
            {
                (*functionBody)->m_envDepth = 0;
            }

            if (deserializeThis && !deserializeNested)
            {
                (*functionBody)->m_isPartialDeserializedFunction = true;
            }
            (*functionBody)->FinishSourceInfo(); // SourceInfo is complete. Register this functionBody to utf8SourceInfo.
            (*functionBody)->m_isFuncRegistered = (bitflags & ffIsFuncRegistered) ? true : false;
            (*functionBody)->m_hasAllNonLocalReferenced = (bitflags & ffhasAllNonLocalReferenced) ? true : false;
            (*functionBody)->m_hasSetIsObject = (bitflags & ffhasSetIsObject) ? true : false;
            (*functionBody)->m_CallsEval = (bitflags & ffhasSetCallsEval) ? true : false;
            (*functionBody)->m_ChildCallsEval = (bitflags & ffChildCallsEval) ? true : false;
            (*functionBody)->m_hasReferenceableBuiltInArguments = (bitflags & ffHasReferenceableBuiltInArguments) ? true : false;
            (*functionBody)->m_isParamAndBodyScopeMerged = (bitflags & ffIsParamAndBodyScopeMerged) ? true : false;
#ifdef ASMJS_PLAT
            (*functionBody)->m_isAsmJsFunction = (bitflags & ffIsAsmJsFunction) ? true : false;
            (*functionBody)->m_isAsmjsMode = (bitflags & ffIsAsmJsMode) ? true : false;
#endif

            if (definedFields->has_loopHeaderArray)
            {
                (*functionBody)->AllocateLoopHeaders();
                auto loopHeaderArray = (*functionBody)->GetLoopHeaderArray();
                uint loopCount = (*functionBody)->GetLoopCount();
                for (uint i = 0; i < loopCount; ++i)
                {
                    uint startOffset, endOffset;
                    current = ReadUInt32(current, &startOffset);
                    current = ReadUInt32(current, &endOffset);
                    loopHeaderArray[i].startOffset = startOffset;
                    loopHeaderArray[i].endOffset = endOffset;
                }
            }

#ifdef ASMJS_PLAT
            if (definedFields->has_asmJsInfo)
            {
                byte asmJsInfoKind;
                current = ReadByte(current, &asmJsInfoKind);
                if (asmJsInfoKind == 1)
                {
                    current = ReadAsmJsFunctionInfo(current, *functionBody);
                }
                else if (asmJsInfoKind == 2)
                {
                    current = ReadAsmJsModuleInfo(current, *functionBody);
                }
                else
                {
                    Assert(false);
                }
            }
#endif

            // Read constants table
#ifdef ASMJS_PLAT
            if ((*functionBody)->GetIsAsmJsFunction())
            {
                current = ReadAsmJsConstantsTable(current, *functionBody);
            }
            else
#endif
            {
                current = ReadConstantsTable(current, *functionBody);
            }

            // Byte code
            current = ReadByteBlock(current, [&functionBody, this](int contentLength, const byte* buffer)
            {
                if (contentLength == 0)
                {
                    (*functionBody)->byteCodeBlock = nullptr;
                }
                else
                {
                    // TODO: Abstract this out to ByteBlock::New
                    (*functionBody)->byteCodeBlock = RecyclerNewLeaf(scriptContext->GetRecycler(), ByteBlock, contentLength, (byte*)buffer);
                }
            });

            // Auxiliary
            if (definedFields->has_auxiliary)
            {
                current = ReadAuxiliary(current, *functionBody);
            }

            if (definedFields->has_propertyIdOfFormals)
            {
                current = ReadPropertyIdOfFormals(current, *functionBody);
            }

            // Inline cache
            current = ReadCacheIdToPropertyIdMap(current, *functionBody);
            current = ReadReferencedPropertyIdMap(current, *functionBody);
            (*functionBody)->AllocateInlineCache();

            if (definedFields->has_slotIdInCachedScopeToNestedIndexArray)
            {
                current = ReadSlotIdInCachedScopeToNestedIndexArray(current, *functionBody);
            }

#if ENABLE_NATIVE_CODEGEN
            if (definedFields->has_callSiteToCallApplyCallSiteArray)
            {
                current = ReadCallSiteToCallApplyCallSiteArray(current, *functionBody);
            }
#endif

            if (definedFields->has_debuggerScopeSlotArray)
            {
                uint debuggerScopeCount = 0;
                current = ReadUInt32(current, &debuggerScopeCount);
                current = ReadSlotArrayDebuggerScopes(current, *functionBody, debuggerScopeCount);
            }

            (*functionBody)->AllocateObjectLiteralTypeArray();

            (*functionBody)->AllocateForInCache();

            // Literal regexes
            (*functionBody)->AllocateLiteralRegexArray();
            for (uint i = 0; i < (*functionBody)->GetLiteralRegexCount(); ++i)
            {
                int length;
                current = ReadInt32(current, &length);
                if (length == -1)
                {
                    Assert(!(*functionBody)->GetLiteralRegex(i));
                    continue;
                }

                int sourceId;
                current = ReadInt32(current, &sourceId);
                const auto source = GetString16ById(sourceId);

                UnifiedRegex::RegexFlags flags;
                CompileAssert(sizeof(flags) == sizeof(byte));
                current = ReadByte(current, reinterpret_cast<byte *>(&flags));
                (*functionBody)->SetLiteralRegex(i, RegexHelper::CompileDynamic(scriptContext, source, length, flags, true));
            }

            // Read source information
            current = ReadSmallSpanSequence(current, &(*functionBody)->m_sourceInfo.pSpanSequence);

            (*functionBody)->executionState.InitializeExecutionModeAndLimits(*functionBody);
        }

        // Read lexically nested functions
        if (nestedCount)
        {
            for(auto i = 0; i<nestedCount; ++i)
            {
                const byte * nestedFunctionBytes = nullptr;
                current = ReadOffsetAsPointer(current, &nestedFunctionBytes);
                if (nestedFunctionBytes == nullptr)
                {
                    (*function)->SetNestedFunc(NULL, i, 0u);
                }
                else
                {
                    FunctionProxy* nestedFunction = nullptr;

                    // If we should deserialize nested functions, go ahead and do so
                    // If we shouldn't, and we're currently deserializing a function proxy
                    // that has been defer-deserialized, simply copy over the function proxy from
                    // from the old function- otherwise, create proxies for the nested functions
                    auto hr = ReadFunctionBody(nestedFunctionBytes, &nestedFunction, sourceInfo, cache, nativeModule, deserializeNested, deserializeNested);
                    if (FAILED(hr))
                    {
                        Assert(0);
                        return hr;
                    }

                    (*function)->SetNestedFunc(nestedFunction->GetFunctionInfo(), i, 0u);
                }
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        // Magical ending
        int constant;
        ReadInt32(current, &constant);
        if (constant != magicEndOfFunctionBody)
        {
            Assert(constant == magicEndOfFunctionBody);
            Throw::FatalInternalError();
        }
#endif
        if (definedFields->has_ConstantCount)
        {
            FunctionBody **functionBody = (FunctionBody **) function;
#if DBG
            if (PHASE_DUMP(Js::DebuggerScopePhase, (*functionBody)))
            {
                (*functionBody)->DumpScopes();
            }
#endif

#if ENABLE_NATIVE_CODEGEN
            if ((!PHASE_OFF(Js::BackEndPhase, *functionBody))
                && !this->scriptContext->GetConfig()->IsNoNative()
                && !(*functionBody)->GetIsAsmjsMode())
            {
                GenerateFunction(this->scriptContext->GetNativeCodeGenerator(), *functionBody);
            }
#endif // ENABLE_NATIVE_CODEGEN

            (*functionBody)->m_isPartialDeserializedFunction = false;
        }
        else if (!isDeferredFunction)
        {
            *function = (*function)->Parse(nullptr, true);
        }

        return S_OK;
    }

    const byte* ReadOneScopeInfo(const byte* current, ByteCodeCache* cache, ScopeInfo** scopeInfo)
    {
        int symbolCount = 0;
        current = ReadInt32(current, &symbolCount);

        Js::LocalFunctionId relativeFunctionId = 0;
        current = ReadUInt32(current, (uint*)&relativeFunctionId);
        FunctionInfo* functionInfo =  cache->LookupFunctionInfo(this->scriptContext, relativeFunctionId);

        Assert(functionInfo != nullptr);

        *scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(), symbolCount * sizeof(ScopeInfo::SymbolInfo), ScopeInfo, functionInfo, symbolCount);

        ScopeInfoFlags scopeInfoFlags;
        current = ReadByte(current, (byte*)&scopeInfoFlags);
        (*scopeInfo)->isDynamic = (scopeInfoFlags & sifIsDynamic) != 0;
        (*scopeInfo)->isObject = (scopeInfoFlags & sifIsObject) != 0;
        (*scopeInfo)->mustInstantiate = (scopeInfoFlags & sifMustInstantiate) != 0;
        (*scopeInfo)->isCached = (scopeInfoFlags & sifIsCached) != 0;
        (*scopeInfo)->hasLocalInClosure = (scopeInfoFlags & sifHasLocalInClosure) != 0;
        (*scopeInfo)->isGeneratorFunctionBody = (scopeInfoFlags & sifIsGeneratorFunctionBody) != 0;
        (*scopeInfo)->isAsyncFunctionBody = (scopeInfoFlags & sifIsAsyncFunctionBody) != 0;
        (*scopeInfo)->areNamesCached = false;

        int scopeType;
        current = ReadInt32(current, &scopeType);
        (*scopeInfo)->scopeType = (::ScopeType)scopeType;

        current = ReadInt32(current, &(*scopeInfo)->scopeId);

        OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("Reading ScopeInfo. Flags: %u. Type: %d. ScopeId: %d. Symbol count: %d\n"), scopeInfoFlags, scopeType, (*scopeInfo)->scopeId, symbolCount);

        for (int i = 0; i < symbolCount; i++)
        {
            ScopeInfo::SymbolInfo* sym = (*scopeInfo)->symbols + i;

            SymbolInfoFlags symbolInfoFlags;
            current = ReadByte(current, (byte*)&symbolInfoFlags);
            sym->hasFuncAssignment = (symbolInfoFlags & syifHasFuncAssignment) != 0;
            sym->isBlockVariable = (symbolInfoFlags & syifIsBlockVariable) != 0;
            sym->isConst = (symbolInfoFlags & syifIsConst) != 0;
            sym->isFuncExpr = (symbolInfoFlags & syifIsFuncExpr) != 0;
            sym->isModuleExportStorage = (symbolInfoFlags & syifIsModuleExportStorage) != 0;
            sym->isModuleImport = (symbolInfoFlags & syifIsModuleImport) != 0;

            current = ReadByte(current, (BYTE*)&sym->symbolType);

            PropertyId obscuredPropertyId;
            current = ReadInt32(current, (int*)&obscuredPropertyId);
            sym->propertyId = cache->LookupPropertyId(obscuredPropertyId);

            OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("\t\tSymbolInfo. Flags: %u. Type: %d. PropertyId: %u\n"), symbolInfoFlags, sym->symbolType, sym->propertyId);
        }

        bool hasParent = false;
        current = ReadBool(current, &hasParent);

        if (hasParent)
        {
            ScopeInfo* parent = nullptr;
            current = ReadScopeInfo(current, cache, &parent);
            (*scopeInfo)->parent = parent;
        }

        return current;
    }

    const byte* ReadScopeInfo(const byte* current, ByteCodeCache* cache, ScopeInfo** scopeInfo)
    {
        LocalScopeInfoId localScopeInfoId;
        current = ReadUInt32(current, (uint*)&localScopeInfoId);

        *scopeInfo = cache->LookupScopeInfo(this->scriptContext, localScopeInfoId);

        return current;
    }

    const byte* ReadDeferredStubs(const byte* current, ByteCodeCache* cache, uint nestedCount, Field(DeferredFunctionStub*)* deferredStubs, bool recurse)
    {
        if (nestedCount == 0)
        {
            return current;
        }

        *deferredStubs = RecyclerNewArray(this->scriptContext->GetRecycler(), DeferredFunctionStub, nestedCount);

        for (uint i = 0; i < nestedCount; i++)
        {
            DeferredFunctionStub* nestedStub = *deferredStubs + i;

            nestedStub->byteCodeCache = cache;

            current = ReadUInt32(current, &nestedStub->ichMin);
            current = ReadUInt32(current, (uint*)&nestedStub->fncFlags);

            serialization_alignment RestorePoint* restorePoint;
            current = ReadStruct<RestorePoint>(current, &restorePoint);
            nestedStub->restorePoint = *restorePoint;

            current = ReadUInt32(current, &nestedStub->capturedNameCount);
            nestedStub->capturedNameSerializedIds = RecyclerNewArray(this->scriptContext->GetRecycler(), int, nestedStub->capturedNameCount);
            for (uint j = 0; j < nestedStub->capturedNameCount; j++)
            {
                current = ReadInt32(current, &nestedStub->capturedNameSerializedIds[j]);
            }

            current = ReadUInt32(current, &nestedStub->nestedCount);

            if (recurse)
            {
                current = ReadDeferredStubs(current, cache, nestedStub->nestedCount, &nestedStub->deferredStubs, recurse);
            }
        }

        return current;
    }

    // Read the top function body.
    HRESULT ReadTopFunctionBody(Field(FunctionBody*)* function, Utf8SourceInfo* sourceInfo, ByteCodeCache * cache, bool allowDefer, NativeModule *nativeModule)
    {
        auto topFunction = ReadInt32(functions, &functionCount);
        firstFunctionId = sourceInfo->GetSrcInfo()->sourceContextInfo->nextLocalFunctionId;
        // this needs to be addressed somehow...need to be able to merge sourcecontexts?
        // it looks like in chakra!Parser::ParseFncDecl, nextLocalFunctionId is what the next function number is, then it is incremented
        // so, if we start with a new/clean ID of 0, then add n to it, then the next
        //sourceInfo->GetSrcInfo()->sourceContextInfo->nextLocalFunctionId += functionCount * 2;
        sourceInfo->GetSrcInfo()->sourceContextInfo->nextLocalFunctionId += functionCount;
        sourceInfo->EnsureInitialized(functionCount);
        sourceInfo->GetSrcInfo()->sourceContextInfo->EnsureInitialized();
        HRESULT hr = E_FAIL;

#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_PREJIT)
        bool prejit = false;
        prejit = (!scriptContext->GetConfig()->IsNoNative() && Js::Configuration::Global.flags.Prejit && nativeModule == nullptr);
        allowDefer = allowDefer && !prejit;
#endif

        FunctionBody* functionBody = NULL;
        hr = ReadFunctionBody(topFunction, (FunctionProxy **)&functionBody, sourceInfo, cache, nativeModule, true, !allowDefer /* don't deserialize nested if defer is allowed */);

        (*function) = functionBody;

        sourceInfo->ClearTopLevelFunctionInfoList();
        sourceInfo->AddTopLevelFunctionInfo(functionBody->GetFunctionInfo(), this->scriptContext->GetRecycler());

#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_PREJIT)
        if (prejit)
        {
            Assert(!allowDefer);
            GenerateAllFunctions(scriptContext->GetNativeCodeGenerator(), functionBody);
        }
#endif
        return hr;
    }


    // Deserialize and save a PropertyIdArray
    const byte *
    DeserializePropertyIdArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto, FunctionBody * functionBody)
    {
        auto serialized = (serialization_alignment const Js::SerializedPropertyIdArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxPropIdArray);
#endif
        auto propertyCount = serialized->propertyCount;
        auto extraSlotCount = serialized->extraSlots;

        Assert(serialized->offset + sizeof(PropertyIdArray) <= deserializeInto->GetLength());
        auto result = (PropertyIdArray *)(deserializeInto->GetBuffer() + serialized->offset);
        result->count = propertyCount;
        result->extraSlots = extraSlotCount;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());
        result->hadDuplicates = serialized->hadDuplicates;
        result->has__proto__ = serialized->has__proto__;

        auto elements = (PropertyId*)(serialized + 1);
        for(int i=0;i<propertyCount;++i)
        {
            result->elements[i] = functionBody->GetByteCodeCache()->LookupPropertyId(elements[i]);
        }
        for(int i=0;i<extraSlotCount;++i)
        {
            result->elements[propertyCount + i] = elements[propertyCount + i];
        }
        auto current = buffer +
            sizeof(serialization_alignment const Js::SerializedPropertyIdArray) + (propertyCount + extraSlotCount) * sizeof(PropertyId);
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxPropIdArray);
#endif
        return current;
    }

    // Deserialize and save a FuncInfoArray
    const byte *
    DeserializeFuncInfoArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const SerializedFuncInfoArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxFuncInfoArray);
#endif
        auto count = serialized->count;

        Assert(serialized->offset + sizeof(AuxArray<FuncInfoEntry>) < deserializeInto->GetLength());
        auto result = (AuxArray<FuncInfoEntry> *)(deserializeInto->GetBuffer() + serialized->offset);
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto elements = (int*)(serialized+1);
        for(int i=0;i<count;++i)
        {
            result->elements[i].nestedIndex = elements[i*2];
            result->elements[i].scopeSlot = elements[i*2+1];
        }
        auto current = buffer + sizeof(serialization_alignment const SerializedFuncInfoArray) + (count * 2 * sizeof(int));
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxFuncInfoArray);
#endif
        return current;
    }

    // Deserialize a var array
    template<typename T>
    const byte * DeserializeVarArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const Js::SerializedVarArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxVarArray);
#endif
        Assert(serialized->offset + sizeof(T) < deserializeInto->GetLength());
        auto result = (T *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->varCount;
        result->SetCount(count);
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            byte code;
            current = ReadByte(current, &code);
            switch(code)
            {
                case ctInt32:
                    {
                        int value;
                        current = ReadConstantSizedInt32(current, &value);
                        result->elements[index] = Js::TaggedInt::ToVarUnchecked(value);
                        break;
                    }
                case ctInt16:
                    {
                        int16 value;
                        current = ReadConstantSizedInt16(current, &value);
                        result->elements[index] = Js::TaggedInt::ToVarUnchecked(value);
                        break;
                    }
                case ctInt8:
                    {
                        int8 value;
                        current = ReadByte(current, (byte *)&value);
                        result->elements[index] = Js::TaggedInt::ToVarUnchecked(value);
                        break;
                    }
                case ctNumber:
                    {
                        double value;
                        current = ReadDouble(current, &value);
                        const auto number = Js::JavascriptNumber::New(value, scriptContext);
#if !FLOATVAR
                        scriptContext->BindReference(number);
#endif
                        result->elements[index] = number;
                        break;
                    }

                default:
                    AssertMsg(UNREACHED, "Unexpected object type in VarArray");
                    Throw::FatalInternalError();
            }
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxVarArray);
#endif
        return current;
    }

    const byte * DeserializeIntArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const Js::SerializedIntArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxIntArray);
#endif
        Assert(serialized->offset + sizeof(AuxArray<int>) < deserializeInto->GetLength());
        auto result = (AuxArray<int> *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->intCount;
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            int32 value;
            current = ReadConstantSizedInt32(current, &value);
            result->elements[index] = value;
        }

#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxIntArray);
#endif
        return current;
    }

    const byte *
    DeserializeFloatArray(ScriptContext * scriptContext, const byte * buffer, ByteBlock * deserializeInto)
    {
        auto serialized = (serialization_alignment const SerializedFloatArray *)buffer;
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        Assert(serialized->magic == magicStartOfAuxFltArray);
#endif
        Assert(serialized->offset + sizeof(AuxArray<double>) < deserializeInto->GetLength());
        auto result = (AuxArray<double> *)(deserializeInto->GetBuffer() + serialized->offset);
        uint count = serialized->floatCount;
        result->count = count;
        Assert(serialized->offset + result->GetDataSize() <= deserializeInto->GetLength());

        auto content = (const byte*)(serialized + 1);
        auto current = content;
        for (uint index = 0; index < count; index++)
        {
            double value;
            current = ReadDouble(current, &value);
            result->elements[index] = value;
        }
#ifdef BYTE_CODE_MAGIC_CONSTANTS
        int magicEnd;
        current = ReadInt32(current, &magicEnd);
        Assert(magicEnd == magicEndOfAuxFltArray);
#endif
        return current;
    }

};

// This constructor is for allocating a ByteCodeCache without a reader (ie: before the bytecode buffer is generated).
// SetReader() should be called before using the cache.
ByteCodeCache::ByteCodeCache(ScriptContext * scriptContext, int builtInPropertyCount)
    : reader(nullptr), propertyCount(0), builtInPropertyCount(builtInPropertyCount), raw(nullptr), propertyIds(nullptr), localFunctionIdToFunctionInfoMap(nullptr), localScopeInfoIdToScopeInfoMap(nullptr), scopeInfoCount(0), scopeInfoRelativeOffsets(nullptr)
{
}

ByteCodeCache::ByteCodeCache(ScriptContext * scriptContext, ByteCodeBufferReader * reader, int builtInPropertyCount)
    : reader(reader), propertyCount(0), builtInPropertyCount(builtInPropertyCount), localFunctionIdToFunctionInfoMap(nullptr), localScopeInfoIdToScopeInfoMap(nullptr), scopeInfoCount(0), scopeInfoRelativeOffsets(nullptr)
{
    Initialize(scriptContext);
}

void ByteCodeCache::SetReader(ScriptContext * scriptContext, ByteCodeBufferReader * reader)
{
    Assert(this->reader == nullptr);
    this->reader = reader;

    Initialize(scriptContext);
}

// Construct the byte code cache. Copy things needed by inline 'Lookup' functions from reader.
void ByteCodeCache::Initialize(ScriptContext * scriptContext)
{
    auto alloc = scriptContext->SourceCodeAllocator();
    propertyCount = reader->string16Count;
    propertyIds = AnewArray(alloc, PropertyId, propertyCount);
    for (auto i = 0; i < propertyCount; ++i)
    {
        propertyIds[i] = -1;
    }

    raw = reader->raw;

    // Read and populate PropertyIds
    for (int i = 0; i < propertyCount; ++i)
    {
        PopulateLookupPropertyId(scriptContext, i);
    }

    scopeInfoCount = reader->scopeInfoCount;
    if (scopeInfoCount > 0)
    {
        scopeInfoRelativeOffsets = AnewArray(alloc, const byte*, scopeInfoCount);

        const byte* current = reader->scopeInfoRelativeOffsets;
        for (uint i = 0; i < scopeInfoCount; i++)
        {
            const byte* ptr = nullptr;
            current = reader->ReadOffsetAsPointer(current, &ptr);

            scopeInfoRelativeOffsets[i] = ptr;
        }
    }
}

// Deserialize and save a PropertyId
void ByteCodeCache::PopulateLookupPropertyId(ScriptContext * scriptContext, int realOffset)
{
    PropertyId idInCache = realOffset + this->builtInPropertyCount;
    bool isPropertyRecord;
    auto propertyName = reader->GetString16ById(idInCache, &isPropertyRecord);
    if(isPropertyRecord)
    {
        auto propertyNameLength = reader->GetString16LengthById(idInCache);

        const Js::PropertyRecord * propertyRecord = scriptContext->GetThreadContext()->GetOrAddPropertyRecordBind(
            JsUtil::CharacterBuffer<char16>(propertyName, propertyNameLength));

        propertyIds[realOffset] = propertyRecord->GetPropertyId();
    }
}

ByteCodeCache::LocalFunctionIdToFunctionInfoMap* ByteCodeCache::EnsureLocalFunctionIdToFunctionInfoMap(ScriptContext * scriptContext)
{
    if (this->localFunctionIdToFunctionInfoMap == nullptr)
    {
        this->localFunctionIdToFunctionInfoMap = Anew(scriptContext->SourceCodeAllocator(), LocalFunctionIdToFunctionInfoMap, scriptContext->SourceCodeAllocator(), 10);
    }
    Assert(this->localFunctionIdToFunctionInfoMap != nullptr);
    return this->localFunctionIdToFunctionInfoMap;
}

void ByteCodeCache::RegisterFunctionIdToFunctionInfo(ScriptContext * scriptContext, LocalFunctionId functionId, FunctionInfo* functionInfo)
{
    EnsureLocalFunctionIdToFunctionInfoMap(scriptContext)->AddNew(functionId, functionInfo);
}

FunctionInfo* ByteCodeCache::LookupFunctionInfo(ScriptContext * scriptContext, LocalFunctionId functionId)
{
    return EnsureLocalFunctionIdToFunctionInfoMap(scriptContext)->Lookup(functionId, nullptr);
}

ByteCodeCache::LocalScopeInfoIdToScopeInfoMap * ByteCodeCache::EnsureLocalScopeInfoIdToScopeInfoMap(ScriptContext * scriptContext)
{
    if (this->localScopeInfoIdToScopeInfoMap == nullptr)
    {
        this->localScopeInfoIdToScopeInfoMap = Anew(scriptContext->SourceCodeAllocator(), LocalScopeInfoIdToScopeInfoMap, scriptContext->SourceCodeAllocator(), 10);
    }
    Assert(this->localScopeInfoIdToScopeInfoMap != nullptr);
    return this->localScopeInfoIdToScopeInfoMap;
}

ScopeInfo* ByteCodeCache::LookupScopeInfo(ScriptContext * scriptContext, LocalScopeInfoId scopeInfoId)
{
    ScopeInfo* scopeInfo = nullptr;
    if (!EnsureLocalScopeInfoIdToScopeInfoMap(scriptContext)->TryGetValue(scopeInfoId, &scopeInfo))
    {
        Assert(scopeInfoId < scopeInfoCount);

        const byte* current = scopeInfoRelativeOffsets[scopeInfoId];
        reader->ReadOneScopeInfo(current, this, &scopeInfo);

        Assert(scopeInfo != nullptr);

        EnsureLocalScopeInfoIdToScopeInfoMap(scriptContext)->AddNew(scopeInfoId, scopeInfo);

        OUTPUT_VERBOSE_TRACE(Js::ByteCodeSerializationPhase, _u("ScopeInfo id %u mapped to 0x%p\n"), scopeInfoId, scopeInfo);
    }
    return scopeInfo;
}

// Serialize function body
HRESULT ByteCodeSerializer::SerializeToBuffer(ScriptContext * scriptContext, ArenaAllocator * alloc, DWORD sourceByteLength, LPCUTF8 utf8Source, FunctionBody * function, SRCINFO const* srcInfo, byte ** buffer, DWORD * bufferBytes, DWORD dwFlags)
{
    int builtInPropertyCount = (dwFlags & GENERATE_BYTE_CODE_BUFFER_LIBRARY) != 0 ?  PropertyIds::_countJSOnlyProperty : TotalNumberOfBuiltInProperties;

    Utf8SourceInfo *utf8SourceInfo = function->GetUtf8SourceInfo();
    HRESULT hr = utf8SourceInfo->EnsureLineOffsetCacheNoThrow();

    if (FAILED(hr))
    {
        return hr;
    }

    ArenaAllocator* codeAllocator = nullptr;
    ByteCodeCache* cache = nullptr;
    DWORD shouldUseCodeAllocator = GENERATE_BYTE_CODE_PARSER_STATE | GENERATE_BYTE_CODE_ALLOC_ANEW;
    if ((dwFlags & shouldUseCodeAllocator) == shouldUseCodeAllocator) // does this apply for cotaskmemalloc as well?
    {
        codeAllocator = scriptContext->SourceCodeAllocator();
        cache = Anew(codeAllocator, ByteCodeCache, scriptContext, builtInPropertyCount);
    }

    int32 sourceCharLength = utf8SourceInfo->GetCchLength();
    ByteCodeBufferBuilder builder(sourceByteLength, sourceCharLength, utf8Source, utf8SourceInfo, scriptContext, alloc, dwFlags, builtInPropertyCount);

    hr = builder.AddTopFunctionBody(function, srcInfo, cache);

    if (SUCCEEDED(hr))
    {
        hr = builder.Create(buffer, bufferBytes);
    }

    if (SUCCEEDED(hr) && cache != nullptr)
    {
        bool isLibraryOrJsBuiltInCode = (dwFlags & GENERATE_BYTE_CODE_BUFFER_LIBRARY) != 0;
        ByteCodeBufferReader* reader = Anew(codeAllocator, ByteCodeBufferReader, scriptContext, *buffer, isLibraryOrJsBuiltInCode, builtInPropertyCount);
        hr = reader->ReadHeader();
        if (FAILED(hr))
        {
            return hr;
        }
        cache->SetReader(scriptContext, reader);
    }

#if INSTRUMENT_BUFFER_INTS
    for (int i = 0; i < 4; i++)
    {
        Output::Print(_u("[BCGENSTATS] %d, %d\n"), i, Counts[i]);
    }
#endif
    return hr;
}

HRESULT ByteCodeSerializer::DeserializeFromBuffer(ScriptContext * scriptContext, uint32 scriptFlags, LPCUTF8 utf8Source, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(FunctionBody*)* function, uint sourceIndex)
{
    return ByteCodeSerializer::DeserializeFromBufferInternal(scriptContext, scriptFlags, utf8Source, /* sourceHolder */ nullptr, srcInfo, buffer, nativeModule, function, sourceIndex);
}
// Deserialize function body from supplied buffer
HRESULT ByteCodeSerializer::DeserializeFromBuffer(ScriptContext * scriptContext, uint32 scriptFlags, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(FunctionBody*)* function, uint sourceIndex)
{
    AssertMsg(sourceHolder != nullptr || sourceIndex != Js::Constants::InvalidSourceIndex, "SourceHolder can't be null, if you have an empty source then pass ISourceHolder::GetEmptySourceHolder()");
    return ByteCodeSerializer::DeserializeFromBufferInternal(scriptContext, scriptFlags, /* utf8Source */ nullptr, sourceHolder, srcInfo, buffer, nativeModule, function, sourceIndex);
}
HRESULT ByteCodeSerializer::DeserializeFromBufferInternal(ScriptContext * scriptContext, uint32 scriptFlags, LPCUTF8 utf8Source, ISourceHolder* sourceHolder, SRCINFO const * srcInfo, byte * buffer, NativeModule *nativeModule, Field(FunctionBody*)* function, uint sourceIndex)
{
    //ETW Event start
    JS_ETW(EventWriteJSCRIPT_BYTECODEDESERIALIZE_START(scriptContext, 0));

    auto alloc = scriptContext->SourceCodeAllocator();
    bool isLibraryCode = ((scriptFlags & fscrIsLibraryCode) == fscrIsLibraryCode);
    bool isJsBuiltInCode = ((scriptFlags & fscrJsBuiltIn) == fscrJsBuiltIn);
    bool isLibraryOrJsBuiltInCode = isLibraryCode || isJsBuiltInCode;
    int builtInPropertyCount = isLibraryOrJsBuiltInCode ? PropertyIds::_countJSOnlyProperty : TotalNumberOfBuiltInProperties;
    auto reader = Anew(alloc, ByteCodeBufferReader, scriptContext, buffer, isLibraryOrJsBuiltInCode, builtInPropertyCount);
    auto hr = reader->ReadHeader();
    if (FAILED(hr))
    {
        return hr;
    }

    ENTER_PINNED_SCOPE(Js::Utf8SourceInfo, sourceInfo);
    ENTER_PINNED_SCOPE(SRCINFO const, pinnedSrcInfo);
    pinnedSrcInfo = srcInfo;

    if(sourceIndex == Js::Constants::InvalidSourceIndex)
    {
        if (sourceHolder == nullptr)
        {
            sourceHolder = utf8Source == nullptr ? ISourceHolder::GetEmptySourceHolder() : RecyclerNew(scriptContext->GetRecycler(), SimpleSourceHolder, utf8Source, reader->sourceSize);
        }

        sourceInfo = Js::Utf8SourceInfo::NewWithHolder(scriptContext, sourceHolder,
            reader->sourceCharLength, pinnedSrcInfo, isLibraryOrJsBuiltInCode);

        reader->utf8SourceInfo = sourceInfo;
        reader->sourceIndex = scriptContext->SaveSourceNoCopy(sourceInfo, reader->sourceCharLength, false);

        sourceInfo->CreateLineOffsetCache(reader->lineCharacterOffsetCacheBuffer, reader->lineByteOffsetCacheBuffer, reader->lineInfoCacheCount);
    }
    else
    {
        Assert(CONFIG_FLAG(ForceSerialized) || ((scriptFlags & fscrCreateParserState) == fscrCreateParserState));
        sourceInfo = scriptContext->GetSource(sourceIndex);
        reader->utf8SourceInfo = sourceInfo;
        reader->sourceIndex = sourceIndex;
    }
    auto cache = Anew(alloc, ByteCodeCache, scriptContext, reader, builtInPropertyCount);

    hr = reader->ReadTopFunctionBody(function, sourceInfo, cache, ((scriptFlags & fscrAllowFunctionProxy) == fscrAllowFunctionProxy), nativeModule);

    //ETW Event stop
    JS_ETW(EventWriteJSCRIPT_BYTECODEDESERIALIZE_STOP(scriptContext,0));

    LEAVE_PINNED_SCOPE();
    LEAVE_PINNED_SCOPE();

    return hr;
}

LPCWSTR ByteCodeSerializer::DeserializeString(const DeferredFunctionStub* deferredStub, uint stringId, uint32& stringLength)
{
    ByteCodeCache* cache = deferredStub->byteCodeCache;
    ByteCodeBufferReader* reader = cache->GetReader();

    stringLength = reader->GetString16LengthById(stringId);
    return reader->GetString16ById(stringId);
}

void ByteCodeSerializer::ReadSourceInfo(const DeferDeserializeFunctionInfo* deferredFunction, int& lineNumber, int& columnNumber, bool& m_isEval, bool& m_isDynamicFunction)
{
    ByteCodeCache* cache = deferredFunction->m_cache;
    ByteCodeBufferReader* reader = cache->GetReader();
    reader->ReadSourceInfo(deferredFunction->m_functionBytes, lineNumber, columnNumber, m_isEval, m_isDynamicFunction);
}

FunctionBody* ByteCodeSerializer::DeserializeFunction(ScriptContext* scriptContext, DeferDeserializeFunctionInfo* deferredFunction)
{
    FunctionBody* deserializedFunctionBody = nullptr;
    ByteCodeCache* cache = deferredFunction->m_cache;
    ByteCodeBufferReader* reader = cache->GetReader();
    HRESULT hr = reader->ReadFunctionBody(deferredFunction->m_functionBytes, (FunctionProxy **)&deserializedFunctionBody, deferredFunction->GetUtf8SourceInfo(), cache, deferredFunction->m_nativeModule, true /* deserialize this */, false /* deserialize nested functions */, deferredFunction);
    if (FAILED(hr))
    {
        // This should never happen as the code is currently
        // structured since we validate the serialized bytecode during creation
        // of function proxies. In the future though, when we reorganize the byte
        // code file format, we could hit this error, in which case we
        // need a strategy to deal with this.
        Assert(false);
        Js::Throw::InternalError();
    }

    return deserializedFunctionBody;
}

SerializedAuxiliary::SerializedAuxiliary( uint offset, SerializedAuxiliaryKind kind ) :
    offset(offset), kind(kind)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , auxMagic(magicStartOfAux)
#endif
{
}


SerializedVarArray::SerializedVarArray( uint offset, bool isVarCount, int varCount ) :
    SerializedAuxiliary(offset, isVarCount ? sakVarArrayVarCount : sakVarArrayIntCount), varCount(varCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxVarArray)
#endif
{

}

SerializedIntArray::SerializedIntArray( uint offset, int intCount ) :
    SerializedAuxiliary(offset, sakIntArray), intCount(intCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxIntArray)
#endif
{

}

SerializedFloatArray::SerializedFloatArray( uint offset, int floatCount ) :
    SerializedAuxiliary(offset, sakFloatArray), floatCount(floatCount)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxFltArray)
#endif
{

}

SerializedPropertyIdArray::SerializedPropertyIdArray( uint offset, int propertyCount, byte extraSlots, bool hadDuplicates, bool has__proto__) :
    SerializedAuxiliary(offset, sakPropertyIdArray), propertyCount(propertyCount), extraSlots(extraSlots), hadDuplicates(hadDuplicates), has__proto__(has__proto__)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxPropIdArray)
#endif
{

}


SerializedFuncInfoArray::SerializedFuncInfoArray( uint offset, int count ) :
    SerializedAuxiliary(offset, sakFuncInfoArray), count(count)
#ifdef BYTE_CODE_MAGIC_CONSTANTS
    , magic(magicStartOfAuxFuncInfoArray)
#endif
{

}

} // namespace Js
